<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Ali Karahisar</title>
	<atom:link href="https://alikarahisar.com/blog/feed/" rel="self" type="application/rss+xml" />
	<link>https://alikarahisar.com/blog/</link>
	<description>Yazılım ve AI Hakkında Karalamalar</description>
	<lastBuildDate>Thu, 29 Oct 2026 07:37:00 +0000</lastBuildDate>
	<language>tr</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.4</generator>

<image>
	<url>https://alikarahisar.com/blog/wp-content/uploads/2026/08/cropped-Code_Generated_Image-32x32.png</url>
	<title>Ali Karahisar</title>
	<link>https://alikarahisar.com/blog/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>SQL Injection: A Friendly Guide to Understanding the Threat</title>
		<link>https://alikarahisar.com/blog/sql-injection-a-friendly-guide-to-understanding-the-threat/</link>
					<comments>https://alikarahisar.com/blog/sql-injection-a-friendly-guide-to-understanding-the-threat/#respond</comments>
		
		<dc:creator><![CDATA[Ali Karahisar]]></dc:creator>
		<pubDate>Thu, 29 Oct 2026 07:37:00 +0000</pubDate>
				<category><![CDATA[Siber Güvenlik]]></category>
		<category><![CDATA[Yazılım]]></category>
		<category><![CDATA[Blind SQL Injection]]></category>
		<category><![CDATA[How SQL Injection works]]></category>
		<category><![CDATA[JDBC SQL Injection prevention]]></category>
		<category><![CDATA[OWASP SQL Injection]]></category>
		<category><![CDATA[Parameterized queries]]></category>
		<category><![CDATA[Prepared statements]]></category>
		<category><![CDATA[Secure database queries]]></category>
		<category><![CDATA[SQL Injection attack]]></category>
		<category><![CDATA[SQL Injection examples]]></category>
		<category><![CDATA[SQL Injection in Java]]></category>
		<category><![CDATA[SQL Injection prevention]]></category>
		<category><![CDATA[SQL Injection vulnerability]]></category>
		<category><![CDATA[What is SQL Injection]]></category>
		<guid isPermaLink="false">https://alikarahisar.com/blog/?p=130</guid>

					<description><![CDATA[<p>Imagine typing your email address into a website&#8217;s login form. The application takes that value, searches its database, and finds your account. Nothing unusual so far. Now imagine entering a piece of SQL instead of an email address. What happens next depends on how the developer built the database query. If the application inserts your [&#8230;]</p>
<p><a href="https://alikarahisar.com/blog/sql-injection-a-friendly-guide-to-understanding-the-threat/">SQL Injection: A Friendly Guide to Understanding the Threat</a> yazısı ilk önce <a href="https://alikarahisar.com/blog">Ali Karahisar</a> üzerinde ortaya çıktı.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Imagine typing your email address into a website&#8217;s login form. The application takes that value, searches its database, and finds your account.</p>



<p class="wp-block-paragraph">Nothing unusual so far.</p>



<p class="wp-block-paragraph">Now imagine entering a piece of SQL instead of an email address. What happens next depends on how the developer built the database query.</p>



<span id="more-130"></span>



<p class="wp-block-paragraph">If the application inserts your input directly into its SQL statement, you might change what the database executes.</p>



<p class="wp-block-paragraph">That&#8217;s the problem behind <strong>SQL Injection</strong>, often shortened to SQLi.</p>



<p class="wp-block-paragraph">Although SQL Injection has been around for decades, developers still encounter it in modern web applications, REST APIs, backend services, and systems that use object-relational mapping frameworks.</p>



<p class="wp-block-paragraph">The good news? Developers can prevent most SQL Injection vulnerabilities through a few reliable coding practices.</p>



<p class="wp-block-paragraph">This guide explains how SQL Injection works, why it matters, and how to write safer Java database code.</p>



<h2 class="wp-block-heading">What Is SQL Injection?</h2>



<p class="wp-block-paragraph">SQL Injection is a security vulnerability that allows an attacker to interfere with a database query by supplying specially crafted input.</p>



<p class="wp-block-paragraph">The vulnerability appears when an application mixes untrusted data with SQL commands.</p>



<p class="wp-block-paragraph">Consider a database query that retrieves a customer by email address:</p>



<pre class="wp-block-code language-sql"><code>SELECT id, email, full_name
FROM customers
WHERE email = 'alice@example.com';</code></pre>



<p class="wp-block-paragraph">This query looks for a specific customer.</p>



<p class="wp-block-paragraph">But what if the application builds the query by joining SQL text with whatever the user enters?</p>



<p class="wp-block-paragraph">An attacker may then influence the query&#8217;s logic instead of simply providing a value.</p>



<p class="wp-block-paragraph">Depending on the application, database permissions, and query structure, SQL Injection can enable attackers to:</p>



<ul class="wp-block-list">
<li>Access sensitive customer information</li>



<li>Read data that should remain private</li>



<li>Modify or delete database records</li>



<li>Bypass certain authentication checks</li>



<li>Interfere with application business logic</li>



<li>Disrupt database availability</li>
</ul>



<p class="wp-block-paragraph">Some database configurations can expose additional capabilities when the application connects with excessive privileges.</p>



<p class="wp-block-paragraph">The OWASP Top 10:2025 includes SQL Injection within <strong>A05:2025 – Injection</strong>, a broader category covering injection vulnerabilities.</p>



<p class="wp-block-paragraph">The important distinction is that SQL itself isn&#8217;t the problem.</p>



<p class="wp-block-paragraph">The vulnerability comes from how an application handles SQL commands and external data.</p>



<h2 class="wp-block-heading">How Does SQL Injection Work?</h2>



<p class="wp-block-paragraph">Let&#8217;s examine a small Java application.</p>



<p class="wp-block-paragraph">Suppose an online store allows users to search for customer information by email address.</p>



<p class="wp-block-paragraph">A developer might write the following JDBC code.</p>



<h3 class="wp-block-heading">A Vulnerable Java Example</h3>



<pre class="wp-block-code language-java"><code>public Customer findByEmail(
        Connection connection,
        String email
) throws SQLException {

    String sql =
        "SELECT id, email, full_name " +
        "FROM customers WHERE email = '" +
        email + "'";

    try (Statement statement =
             connection.createStatement();
         ResultSet rs =
             statement.executeQuery(sql)) {

        if (rs.next()) {
            return new Customer(
                rs.getLong("id"),
                rs.getString("email"),
                rs.getString("full_name")
            );
        }

        return null;
    }
}</code></pre>



<p class="wp-block-paragraph">The code appears straightforward.</p>



<p class="wp-block-paragraph">It creates a SQL query, executes it, and retrieves the first matching customer.</p>



<p class="wp-block-paragraph">Unfortunately, one line introduces a serious security weakness:</p>



<pre class="wp-block-code language-sql"><code>"FROM customers WHERE email = '" + email + "'"</code></pre>



<p class="wp-block-paragraph">The application inserts the supplied email address directly into the SQL command.</p>



<p class="wp-block-paragraph">That means the value becomes part of the SQL statement&#8217;s text.</p>



<p class="wp-block-paragraph">The database cannot reliably distinguish the developer&#8217;s intended query structure from SQL syntax that arrived through the user input.</p>



<h3 class="wp-block-heading">What Happens When an Attacker Changes the Input?</h3>



<p class="wp-block-paragraph">Instead of a normal email address, consider this input:</p>



<pre class="wp-block-code language-sql"><code>' OR '1'='1'</code></pre>



<p class="wp-block-paragraph">When the application concatenates it into the original query, the final SQL becomes:</p>



<pre class="wp-block-code language-sql"><code>SELECT id, email, full_name
FROM customers
WHERE email = '' OR '1'='1';</code></pre>



<p class="wp-block-paragraph">Look closely at the condition:</p>



<pre class="wp-block-code language-sql"><code>'1'='1'</code></pre>



<p class="wp-block-paragraph">This comparison always evaluates to true.</p>



<p class="wp-block-paragraph">As a result, the database may return every row instead of looking for one customer.</p>



<p class="wp-block-paragraph">The sample Java method reads only the first result, but the query itself no longer applies the intended email filter.</p>



<p class="wp-block-paragraph">If other application logic treats that result as proof of identity or authorization, the consequences could become much more serious.</p>



<p class="wp-block-paragraph">This example captures the central idea behind SQL Injection:</p>



<p class="wp-block-paragraph"><strong>The attacker turns input data into executable SQL logic.</strong></p>



<h2 class="wp-block-heading">Why Is SQL Injection Dangerous?</h2>



<p class="wp-block-paragraph">SQL Injection can affect more than a single database query.</p>



<p class="wp-block-paragraph">Its impact depends on the application&#8217;s architecture, the affected SQL statement, and the privileges assigned to the database connection.</p>



<h3 class="wp-block-heading">Exposure of Sensitive Data</h3>



<p class="wp-block-paragraph">Applications often store customer details, addresses, order histories, and other valuable information.</p>



<p class="wp-block-paragraph">A vulnerable query can expose data that users should never access.</p>



<p class="wp-block-paragraph">Even when an attacker cannot download an entire database, accessing another customer&#8217;s records creates a security and privacy problem.</p>



<h3 class="wp-block-heading">Unauthorized Data Modification</h3>



<p class="wp-block-paragraph">Some application accounts can create, update, or delete database records.</p>



<p class="wp-block-paragraph">If an attacker exploits a suitable SQL Injection vulnerability through one of these accounts, the attacker may gain opportunities to modify business data.</p>



<p class="wp-block-paragraph">For an online store, that might affect product information, customer records, or order management processes.</p>



<h3 class="wp-block-heading">Application Disruption</h3>



<p class="wp-block-paragraph">A maliciously altered query may consume excessive database resources.</p>



<p class="wp-block-paragraph">Expensive operations can reduce performance and interfere with legitimate requests.</p>



<p class="wp-block-paragraph">Under certain conditions, this behavior can contribute to a denial-of-service incident.</p>



<h3 class="wp-block-heading">Authentication and Authorization Risks</h3>



<p class="wp-block-paragraph">SQL Injection can sometimes interfere with authentication or authorization checks.</p>



<p class="wp-block-paragraph">However, not every SQL Injection vulnerability gives an attacker the ability to log in as another user.</p>



<p class="wp-block-paragraph">The actual outcome depends on how the application uses the query result and which permissions the database account holds.</p>



<h2 class="wp-block-heading">Common Types of SQL Injection</h2>



<p class="wp-block-paragraph">Security professionals commonly classify SQL Injection techniques by how attackers retrieve information or observe the application&#8217;s behavior.</p>



<h3 class="wp-block-heading">1. In-Band SQL Injection</h3>



<p class="wp-block-paragraph">In-band SQL Injection uses the same communication channel to influence the SQL query and observe its output.</p>



<p class="wp-block-paragraph">Two common forms exist.</p>



<p class="wp-block-paragraph"><strong>Error-Based SQL Injection</strong></p>



<p class="wp-block-paragraph">Attackers examine database error messages to learn about the underlying system.</p>



<p class="wp-block-paragraph">Detailed error responses may reveal query structure, table names, or other technical information.</p>



<p class="wp-block-paragraph"><strong>Union-Based SQL Injection</strong></p>



<p class="wp-block-paragraph">Attackers attempt to combine the results of different SQL queries through the <code>UNION</code> operator.</p>



<p class="wp-block-paragraph">The technique requires compatible query structures and data types.</p>



<h3 class="wp-block-heading">2. Blind SQL Injection</h3>



<p class="wp-block-paragraph">Some applications hide database errors and query output.</p>



<p class="wp-block-paragraph">That doesn&#8217;t automatically make them immune to SQL Injection.</p>



<p class="wp-block-paragraph">Attackers may still infer information by observing application behavior.</p>



<p class="wp-block-paragraph"><strong>Boolean-Based Blind SQL Injection</strong></p>



<p class="wp-block-paragraph">This technique compares how an application responds to conditions that evaluate to true or false.</p>



<p class="wp-block-paragraph"><strong>Time-Based Blind SQL Injection</strong></p>



<p class="wp-block-paragraph">This technique relies on differences in response times.</p>



<p class="wp-block-paragraph">Since network latency and server load can introduce noise, timing observations require careful interpretation.</p>



<h3 class="wp-block-heading">3. Out-of-Band SQL Injection</h3>



<p class="wp-block-paragraph">Out-of-band techniques attempt to use a separate communication channel to retrieve information.</p>



<p class="wp-block-paragraph">These attacks depend on particular database capabilities, network access, and configuration choices.</p>



<p class="wp-block-paragraph">Not every database environment supports the conditions they require.</p>



<h3 class="wp-block-heading">4. Second-Order SQL Injection</h3>



<p class="wp-block-paragraph">Second-order SQL Injection often surprises developers.</p>



<p class="wp-block-paragraph">An application first stores untrusted input in its database.</p>



<p class="wp-block-paragraph">Later, another part of the application retrieves that stored value and inserts it into a new SQL query without proper parameter binding.</p>



<p class="wp-block-paragraph">The vulnerability appears during the second operation rather than the original request.</p>



<p class="wp-block-paragraph">This illustrates an important security principle: data doesn&#8217;t automatically become trustworthy because it came from your own database.</p>



<h2 class="wp-block-heading">How to Prevent SQL Injection</h2>



<p class="wp-block-paragraph">The most effective approach starts with one rule:</p>



<p class="wp-block-paragraph"><strong>Keep SQL commands separate from data values.</strong></p>



<p class="wp-block-paragraph">OWASP recommends parameterized queries as a primary defense.</p>



<p class="wp-block-paragraph">Let&#8217;s examine what that means in practice.</p>



<h3 class="wp-block-heading">1. Use Prepared Statements and Parameterized Queries</h3>



<p class="wp-block-paragraph">Java provides the <code>PreparedStatement</code> interface through JDBC.</p>



<p class="wp-block-paragraph">Instead of embedding a value inside SQL text, we define the SQL statement and bind the value separately.</p>



<p class="wp-block-paragraph">Here&#8217;s a safer version of our customer lookup.</p>



<pre class="wp-block-code language-java"><code>public Customer findByEmail(
        Connection connection,
        String email
) throws SQLException {

    String sql = """
        SELECT id, email, full_name
        FROM customers
        WHERE email = ?
        """;

    try (PreparedStatement statement =
             connection.prepareStatement(sql)) {

        statement.setString(1, email);

        try (ResultSet rs =
                 statement.executeQuery()) {

            if (rs.next()) {
                return new Customer(
                    rs.getLong("id"),
                    rs.getString("email"),
                    rs.getString("full_name")
                );
            }

            return null;
        }
    }
}</code></pre>



<p class="wp-block-paragraph">Two details matter here.</p>



<p class="wp-block-paragraph">First, the <code>?</code> placeholder represents a data value.</p>



<p class="wp-block-paragraph">Second, <code>setString()</code> binds the supplied email address to that placeholder.</p>



<p class="wp-block-paragraph">The JDBC driver handles the value through the database parameter-binding mechanism.</p>



<p class="wp-block-paragraph">Even if someone enters:</p>



<pre class="wp-block-code language-sql"><code>' OR '1'='1'</code></pre>



<p class="wp-block-paragraph">the database treats that input as a string value to search for rather than additional SQL logic.</p>



<h3 class="wp-block-heading">Why Do Prepared Statements Work?</h3>



<p class="wp-block-paragraph">Prepared statements establish a boundary between the SQL statement&#8217;s structure and its data values.</p>



<p class="wp-block-paragraph">The application defines the query.</p>



<p class="wp-block-paragraph">The user supplies the values.</p>



<p class="wp-block-paragraph">That difference prevents data-bound input from changing the query&#8217;s intended syntax.</p>



<p class="wp-block-paragraph">It also handles legitimate values containing special characters, such as a customer&#8217;s name with an apostrophe.</p>



<p class="wp-block-paragraph">One limitation matters, though: prepared statements protect the values you bind. They cannot protect SQL fragments that you still construct through unsafe concatenation.</p>



<h3 class="wp-block-heading">2. Use Allow-Lists for Dynamic Query Structure</h3>



<p class="wp-block-paragraph">Parameterized queries work for data values, but they don&#8217;t replace SQL identifiers or keywords.</p>



<p class="wp-block-paragraph">For example, a JDBC <code>?</code> placeholder cannot represent a column name in an <code>ORDER BY</code> clause.</p>



<p class="wp-block-paragraph">Suppose your online store allows customers to sort products by price or name.</p>



<p class="wp-block-paragraph">Avoid this approach:</p>



<pre class="wp-block-code language-java"><code>String sql =
    "SELECT id, name, price FROM products ORDER BY "
    + sortBy;</code></pre>



<p class="wp-block-paragraph">Here, the application inserts untrusted input into the SQL statement&#8217;s structure.</p>



<p class="wp-block-paragraph">Instead, define which options the application supports.</p>



<pre class="wp-block-code language-java"><code>public String buildProductQuery(
        String sortBy,
        String direction
) {
    String column = switch (sortBy) {
        case "name"  -&gt; "name";
        case "price" -&gt; "price";
        default -&gt; throw new
            IllegalArgumentException(
                "Invalid sorting field"
            );
    };

    String order = switch (direction) {
        case "asc"  -&gt; "ASC";
        case "desc" -&gt; "DESC";
        default -&gt; throw new
            IllegalArgumentException(
                "Invalid sorting direction"
            );
    };

    return """
        SELECT id, name, price
        FROM products
        ORDER BY %s %s
        """.formatted(column, order);
}</code></pre>



<p class="wp-block-paragraph">The application now controls the SQL identifiers and keywords.</p>



<p class="wp-block-paragraph">Users can request only the choices the developer explicitly supports.</p>



<p class="wp-block-paragraph">Continue using prepared statements for the query&#8217;s ordinary data values.</p>



<h3 class="wp-block-heading">3. Validate Input According to Business Rules</h3>



<p class="wp-block-paragraph">Input validation helps applications reject unexpected values.</p>



<p class="wp-block-paragraph">For example, an application can require a product identifier to contain a positive integer or limit the length of a search term.</p>



<p class="wp-block-paragraph">Those checks improve reliability and reduce unnecessary processing.</p>



<p class="wp-block-paragraph">However, input validation alone doesn&#8217;t prevent SQL Injection.</p>



<p class="wp-block-paragraph">Blocking quotation marks or maintaining a blacklist of suspicious SQL keywords cannot reliably address every SQL dialect and query context.</p>



<p class="wp-block-paragraph">Use validation for business rules and parameter binding for database query safety.</p>



<h3 class="wp-block-heading">4. Apply the Principle of Least Privilege</h3>



<p class="wp-block-paragraph">Database accounts should have only the permissions their applications actually need.</p>



<p class="wp-block-paragraph">For example, a service that reads product information shouldn&#8217;t require permission to modify every customer record.</p>



<p class="wp-block-paragraph">A PostgreSQL administrator could restrict table permissions with statements such as:</p>



<pre class="wp-block-code language-sql"><code>REVOKE ALL ON TABLE products
FROM product_reader;

GRANT SELECT ON TABLE products
TO product_reader;</code></pre>



<p class="wp-block-paragraph">These statements control permissions on the specified table. A production security review must also consider role inheritance, schema permissions, and access to other database objects.</p>



<p class="wp-block-paragraph">Least privilege doesn&#8217;t remove a SQL Injection vulnerability.</p>



<p class="wp-block-paragraph">Instead, it reduces the potential damage if an attacker successfully exploits one.</p>



<p class="wp-block-paragraph">For larger architectures, consider different database roles for services with different responsibilities.</p>



<p class="wp-block-paragraph">A reporting service and an order-processing service rarely need identical database privileges.</p>



<h3 class="wp-block-heading">5. Don&#8217;t Assume Your ORM Makes Every Query Safe</h3>



<p class="wp-block-paragraph">Developers often use Hibernate, JPA, or similar frameworks to avoid writing repetitive SQL code.</p>



<p class="wp-block-paragraph">These frameworks support parameterized query APIs, but developers can still introduce vulnerabilities through unsafe query construction.</p>



<p class="wp-block-paragraph">Consider this JPQL example:</p>



<pre class="wp-block-code language-java"><code>String jpql =
    "SELECT u FROM User u WHERE u.email = '"
    + email + "'";

var query = entityManager.createQuery(
    jpql,
    User.class
);</code></pre>



<p class="wp-block-paragraph">The application still inserts untrusted input into the query text.</p>



<p class="wp-block-paragraph">A better approach uses a named parameter.</p>



<pre class="wp-block-code language-java"><code>var query = entityManager.createQuery(
    """
    SELECT u
    FROM User u
    WHERE u.email = :email
    """,
    User.class
);

query.setParameter("email", email);

List&lt;User&gt; users = query.getResultList();</code></pre>



<p class="wp-block-paragraph">Here, the ORM treats the email value as a parameter.</p>



<p class="wp-block-paragraph">Review dynamically constructed JPQL, HQL, native SQL, and sorting expressions with the same attention you give to ordinary JDBC queries.</p>



<h3 class="wp-block-heading">6. Review Stored Procedures Carefully</h3>



<p class="wp-block-paragraph">Stored procedures can help organize database logic.</p>



<p class="wp-block-paragraph">However, they don&#8217;t automatically eliminate SQL Injection.</p>



<p class="wp-block-paragraph">A procedure that constructs dynamic SQL by concatenating untrusted values can create the same vulnerability as application-level string concatenation.</p>



<p class="wp-block-paragraph">Use bound arguments when calling procedures.</p>



<p class="wp-block-paragraph">Inside the procedure, prefer static SQL or parameterized dynamic SQL.</p>



<p class="wp-block-paragraph">When a procedure needs a dynamic identifier, restrict the selection to trusted, application-approved choices.</p>



<h3 class="wp-block-heading">7. Handle Database Errors and Logs Safely</h3>



<p class="wp-block-paragraph">Database error messages help developers diagnose problems.</p>



<p class="wp-block-paragraph">But detailed SQL errors can reveal useful information to attackers.</p>



<p class="wp-block-paragraph">Production applications should avoid returning raw database exceptions to clients.</p>



<p class="wp-block-paragraph">Keep technical error details in protected logs, and provide clients with appropriate generic error responses.</p>



<p class="wp-block-paragraph">Make sure your logging strategy also protects sensitive information.</p>



<p class="wp-block-paragraph">Passwords, authentication tokens, and private customer data shouldn&#8217;t appear in ordinary diagnostic logs.</p>



<h2 class="wp-block-heading">How Can Developers Test for SQL Injection?</h2>



<p class="wp-block-paragraph">You should test only applications and environments you own or have explicit authorization to assess.</p>



<p class="wp-block-paragraph">Start with code review and repeatable automated checks.</p>



<h3 class="wp-block-heading">Review Query Construction</h3>



<p class="wp-block-paragraph">Look for places where the application builds SQL statements dynamically.</p>



<p class="wp-block-paragraph">Pay particular attention to:</p>



<ul class="wp-block-list">
<li>String concatenation inside SQL queries</li>



<li>Dynamic JDBC <code>Statement</code> execution</li>



<li>Native SQL constructed through ORM frameworks</li>



<li>Dynamic JPQL or HQL expressions</li>



<li>Stored procedures that assemble SQL text</li>



<li>User-controlled sorting fields and identifiers</li>
</ul>



<p class="wp-block-paragraph">The presence of <code>Statement</code> alone doesn&#8217;t prove a vulnerability. The risk arises when untrusted data can modify SQL syntax.</p>



<h3 class="wp-block-heading">Combine SAST and DAST</h3>



<p class="wp-block-paragraph">Static Application Security Testing (SAST) tools inspect source code for potential vulnerabilities.</p>



<p class="wp-block-paragraph">Dynamic Application Security Testing (DAST) tools examine a running application&#8217;s behavior.</p>



<p class="wp-block-paragraph">Tools such as OWASP ZAP can support an authorized testing process.</p>



<p class="wp-block-paragraph">Neither category guarantees complete coverage.</p>



<p class="wp-block-paragraph">Use automated testing alongside code review and carefully designed regression tests.</p>



<h3 class="wp-block-heading">Write a Java Regression Test</h3>



<p class="wp-block-paragraph">A simple JUnit 5 test can verify that your repository treats suspicious input as ordinary data.</p>



<pre class="wp-block-code language-java"><code>@Test
void shouldNotInterpretInputAsSql()
        throws SQLException {

    String input = "' OR '1'='1";

    Customer customer =
        repository.findByEmail(
            connection,
            input
        );

    assertNull(customer);
}</code></pre>



<p class="wp-block-paragraph">For this test, use an isolated database with known records and no customer whose email equals the supplied test string.</p>



<p class="wp-block-paragraph">You can strengthen the test by inserting a legitimate customer first and verifying that the suspicious input doesn&#8217;t return that record.</p>



<p class="wp-block-paragraph">This doesn&#8217;t replace broader security testing.</p>



<p class="wp-block-paragraph">It does, however, help prevent a previously corrected vulnerability from returning during a future code change.</p>



<h2 class="wp-block-heading">A Real-World Example: Securing an E-Commerce Product Search</h2>



<p class="wp-block-paragraph">An e-commerce application exposes many database-driven operations.</p>



<p class="wp-block-paragraph">Product searches, price filters, order histories, customer profiles, and administrative reports all deserve attention.</p>



<p class="wp-block-paragraph">Let&#8217;s implement a simple product search using Java and JDBC.</p>



<p class="wp-block-paragraph">The method accepts a keyword and a maximum price.</p>



<pre class="wp-block-code language-java"><code>public List&lt;Product&gt; searchProducts(
        Connection connection,
        String keyword,
        BigDecimal maxPrice
) throws SQLException {

    String sql = """
        SELECT id, name, price
        FROM products
        WHERE LOWER(name) LIKE LOWER(?)
          AND price &lt;= ?
        """;

    List&lt;Product&gt; products =
        new ArrayList&lt;&gt;();

    try (PreparedStatement statement =
             connection.prepareStatement(sql)) {

        statement.setString(
            1,
            "%" + keyword + "%"
        );

        statement.setBigDecimal(
            2,
            maxPrice
        );

        try (ResultSet rs =
                 statement.executeQuery()) {

            while (rs.next()) {
                products.add(
                    new Product(
                        rs.getLong("id"),
                        rs.getString("name"),
                        rs.getBigDecimal("price")
                    )
                );
            }
        }
    }

    return products;
}</code></pre>



<p class="wp-block-paragraph">This example binds both user-controlled values.</p>



<p class="wp-block-paragraph">The first parameter searches for products containing the requested keyword.</p>



<p class="wp-block-paragraph">The second parameter limits the maximum price.</p>



<p class="wp-block-paragraph">Notice that the code adds <code>%</code> characters to the Java string before binding it.</p>



<p class="wp-block-paragraph">That&#8217;s safe from SQL Injection because the application constructs a parameter value rather than adding SQL syntax to the query.</p>



<p class="wp-block-paragraph">There is one additional detail worth understanding.</p>



<p class="wp-block-paragraph">SQL <code>LIKE</code> treats <code>%</code> and <code>_</code> as wildcard characters. If your application expects literal text matching, you should handle those characters according to your database&#8217;s escaping rules.</p>



<p class="wp-block-paragraph">That&#8217;s a search-behavior concern rather than an SQL Injection vulnerability, but it demonstrates why secure code must also follow the application&#8217;s business requirements.</p>



<h2 class="wp-block-heading">Frequently Asked Questions About SQL Injection</h2>



<h3 class="wp-block-heading">Is SQL Injection still a problem in modern applications?</h3>



<p class="wp-block-paragraph">Yes. Modern frameworks provide safer database APIs, but developers can still introduce SQL Injection vulnerabilities when they concatenate untrusted values into query text.</p>



<h3 class="wp-block-heading">Does SQL Injection affect only login forms?</h3>



<p class="wp-block-paragraph">No. Search fields, filters, reports, API parameters, and other database-driven operations may expose vulnerable queries.</p>



<h3 class="wp-block-heading">Can prepared statements completely prevent SQL Injection?</h3>



<p class="wp-block-paragraph">Correctly parameterized data values cannot change the SQL statement&#8217;s structure. However, developers can still create vulnerabilities by constructing other query fragments from untrusted input.</p>



<h3 class="wp-block-heading">Does Hibernate prevent SQL Injection automatically?</h3>



<p class="wp-block-paragraph">No. Hibernate supports secure parameter binding, but unsafe dynamically constructed HQL, JPQL, or native SQL can introduce vulnerabilities.</p>



<h3 class="wp-block-heading">Should I remove special characters from user input?</h3>



<p class="wp-block-paragraph">Not as a general SQL Injection defense. Legitimate values may contain apostrophes and other special characters. Use parameter binding instead of relying on character filtering.</p>



<h3 class="wp-block-heading">Can a Web Application Firewall stop SQL Injection?</h3>



<p class="wp-block-paragraph">A WAF may detect or block some malicious requests, but it cannot guarantee protection and doesn&#8217;t fix the underlying code defect. Treat it as an additional security layer.</p>



<h3 class="wp-block-heading">What&#8217;s the difference between SQL Injection and XSS?</h3>



<p class="wp-block-paragraph">SQL Injection targets the structure of database queries. Cross-Site Scripting (XSS) targets how browsers interpret untrusted content. Both involve trust boundaries, but they affect different execution contexts.</p>



<h2 class="wp-block-heading">Final Thoughts: Make Safe Queries Your Default</h2>



<p class="wp-block-paragraph">SQL Injection becomes possible when an application allows external data to change the structure of a database query.</p>



<p class="wp-block-paragraph">You don&#8217;t need an elaborate security system to address the core problem.</p>



<p class="wp-block-paragraph">Start by writing parameterized queries. Use explicit allow-lists when users need to influence query structure. Limit database privileges and include security checks in your development workflow.</p>



<p class="wp-block-paragraph">If you&#8217;re writing Java, make <code>PreparedStatement</code> your default choice for JDBC queries that include variable data.</p>



<p class="wp-block-paragraph">If you&#8217;re using an ORM, learn how its parameter-binding APIs work and avoid unsafe dynamic query construction.</p>



<p class="wp-block-paragraph">Most importantly, keep one principle in mind:</p>



<p class="wp-block-paragraph"><strong>Your application defines the SQL. Your users provide the data.</strong></p>



<p class="wp-block-paragraph">That separation is the foundation of SQL Injection prevention.</p>



<h2 class="wp-block-heading">References and Further Reading</h2>



<ol start="1" class="wp-block-list">
<li>OWASP — SQL Injection Prevention Cheat Sheet<br><a href="https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html">https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html</a></li>



<li>OWASP — Top 10:2025, A05 Injection<br><a href="https://top10.owasp.org/2025/A05_2025-Injection/">https://top10.owasp.org/2025/A05_2025-Injection/</a></li>



<li>OWASP — Web Security Testing Guide<br><a href="https://owasp.org/www-project-web-security-testing-guide/">https://owasp.org/www-project-web-security-testing-guide/</a></li>



<li>Oracle — Using Prepared Statements<br><a href="https://docs.oracle.com/javase/tutorial/jdbc/basics/prepared.html">https://docs.oracle.com/javase/tutorial/jdbc/basics/prepared.html</a></li>
</ol>



<p class="wp-block-paragraph"></p>
<p><a href="https://alikarahisar.com/blog/sql-injection-a-friendly-guide-to-understanding-the-threat/">SQL Injection: A Friendly Guide to Understanding the Threat</a> yazısı ilk önce <a href="https://alikarahisar.com/blog">Ali Karahisar</a> üzerinde ortaya çıktı.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://alikarahisar.com/blog/sql-injection-a-friendly-guide-to-understanding-the-threat/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>SQL Injection Nedir? Saldırıyı Anlamak ve Önlemek İçin Pratik Rehber</title>
		<link>https://alikarahisar.com/blog/sql-injection-nedir/</link>
					<comments>https://alikarahisar.com/blog/sql-injection-nedir/#respond</comments>
		
		<dc:creator><![CDATA[Ali Karahisar]]></dc:creator>
		<pubDate>Thu, 29 Oct 2026 07:29:14 +0000</pubDate>
				<category><![CDATA[Siber Güvenlik]]></category>
		<category><![CDATA[Yazılım]]></category>
		<category><![CDATA[Blind SQL Injection]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[OWASP SQL Injection]]></category>
		<category><![CDATA[Parametreli SQL sorguları]]></category>
		<category><![CDATA[PreparedStatement Java]]></category>
		<category><![CDATA[SQL enjeksiyonu]]></category>
		<category><![CDATA[SQL güvenlik açığı]]></category>
		<category><![CDATA[SQL Injection güvenlik testi]]></category>
		<category><![CDATA[SQL Injection nasıl çalışır]]></category>
		<category><![CDATA[SQL Injection nasıl önlenir]]></category>
		<category><![CDATA[SQL Injection nedir]]></category>
		<category><![CDATA[SQL Injection örnekleri]]></category>
		<category><![CDATA[SQL Injection saldırısı]]></category>
		<guid isPermaLink="false">https://alikarahisar.com/blog/?p=113</guid>

					<description><![CDATA[<p>Bir web uygulamasının giriş ekranına kullanıcı adınızı yazdığınızı düşünün. Uygulama bu bilgiyi veritabanına gönderiyor ve hesabınızı arıyor. Buraya kadar her şey normal. Peki, kullanıcı adı alanına normal bir isim yerine SQL komutu yazarsanız ne olur? Uygulama bu girdiyi doğrudan SQL sorgusuna ekliyorsa veritabanı beklediğinizden farklı bir komut çalıştırabilir. Böylece basit bir metin alanı, veritabanı güvenliğini [&#8230;]</p>
<p><a href="https://alikarahisar.com/blog/sql-injection-nedir/">SQL Injection Nedir? Saldırıyı Anlamak ve Önlemek İçin Pratik Rehber</a> yazısı ilk önce <a href="https://alikarahisar.com/blog">Ali Karahisar</a> üzerinde ortaya çıktı.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Bir web uygulamasının giriş ekranına kullanıcı adınızı yazdığınızı düşünün. Uygulama bu bilgiyi veritabanına gönderiyor ve hesabınızı arıyor. Buraya kadar her şey normal.</p>



<p class="wp-block-paragraph">Peki, kullanıcı adı alanına normal bir isim yerine SQL komutu yazarsanız ne olur?</p>



<span id="more-113"></span>



<p class="wp-block-paragraph">Uygulama bu girdiyi doğrudan SQL sorgusuna ekliyorsa veritabanı beklediğinizden farklı bir komut çalıştırabilir. Böylece basit bir metin alanı, veritabanı güvenliğini tehdit eden bir noktaya dönüşür.</p>



<p class="wp-block-paragraph">Bu güvenlik açığına <strong>SQL Injection (SQL enjeksiyonu)</strong> adını veriyoruz.</p>



<p class="wp-block-paragraph">SQL Injection yalnızca eski web uygulamalarının sorunu değil. Modern Java uygulamaları, REST API&#8217;ler, mikroservisler ve ORM kullanan sistemler de hatalı sorgu oluşturduklarında aynı riskle karşılaşır.</p>



<p class="wp-block-paragraph">Bu rehberde SQL Injection&#8217;ın nasıl çalıştığını, hangi hataların güvenlik açığı oluşturduğunu ve Java ile güvenli veritabanı sorgularını nasıl geliştirebileceğinizi inceleyeceğiz.</p>



<h2 class="wp-block-heading">SQL Injection Nedir?</h2>



<p class="wp-block-paragraph">SQL Injection, saldırganın uygulamanın SQL sorgusuna kendi girdisi üzerinden müdahale etmesini sağlayan bir güvenlik açığıdır.</p>



<p class="wp-block-paragraph">Sorunun temelinde SQL komutları ile kullanıcı verilerinin birbirine karışması yatar.</p>



<p class="wp-block-paragraph">Bir uygulama kullanıcı girdisini SQL sorgusunun metnine doğrudan eklediğinde, saldırgan sorgunun mantığını değiştirebilir.</p>



<p class="wp-block-paragraph">Bu durum saldırgana uygulamanın veritabanı izinleri ve sorgunun yapısına bağlı olarak şu fırsatları verebilir:</p>



<ul class="wp-block-list">
<li>Normalde erişemeyeceği verilere ulaşmak</li>



<li>Kullanıcı hesapları ve müşteri bilgilerini okumak</li>



<li>Yetkisiz veri değişiklikleri yapmak</li>



<li>Kayıtları silmek veya bozmak</li>



<li>Kimlik doğrulama kontrollerini aşmak</li>



<li>Veritabanı hizmetinin çalışmasını olumsuz etkilemek</li>
</ul>



<p class="wp-block-paragraph">Bazı veritabanı özellikleri ve yüksek yetkili hesaplar, saldırının etkisini veritabanının dışına da taşıyabilir.</p>



<p class="wp-block-paragraph">SQL Injection, OWASP Top 10:2025 listesindeki <strong>A05:2025 – Injection</strong> kategorisinin kapsamına girer.</p>



<p class="wp-block-paragraph">Burada önemli nokta şu: SQL dili tek başına güvenlik açığı oluşturmaz. Açığı, uygulamanın güvenilmeyen verileri SQL sözdiziminin parçası hâline getirmesi oluşturur.</p>



<h2 class="wp-block-heading">SQL Injection Nasıl Çalışır?</h2>



<p class="wp-block-paragraph">Bir e-ticaret uygulamasında kullanıcıların e-posta adresleri üzerinden hesap bilgilerini aradığımızı düşünelim.</p>



<p class="wp-block-paragraph">Normal koşullarda uygulama şu sorguyu çalıştırır:</p>



<pre class="wp-block-code language-sql"><code>SELECT id, email, full_name
FROM customers
WHERE email = 'ali@example.com';</code></pre>



<p class="wp-block-paragraph">Veritabanı yalnızca ilgili e-posta adresini taşıyan kaydı döndürür.</p>



<p class="wp-block-paragraph">Şimdi Java tarafında SQL sorgusunu nasıl oluşturduğumuza bakalım.</p>



<h3 class="wp-block-heading">Güvensiz Java Kod Örneği</h3>



<pre class="wp-block-code language-java"><code>public Customer findByEmail(
        Connection connection,
        String email
) throws SQLException {

    String sql = "SELECT id, email, full_name " +
                 "FROM customers WHERE email = '" +
                 email + "'";

    try (Statement statement =
             connection.createStatement();
         ResultSet rs = statement.executeQuery(sql)) {

        if (rs.next()) {
            return new Customer(
                rs.getLong("id"),
                rs.getString("email"),
                rs.getString("full_name")
            );
        }

        return null;
    }
}</code></pre>



<p class="wp-block-paragraph">İlk bakışta kod oldukça basit. Geliştirici e-posta adresini SQL sorgusuna ekliyor ve sorguyu çalıştırıyor.</p>



<p class="wp-block-paragraph">Ancak şu satır ciddi bir güvenlik problemi yaratıyor:</p>



<pre class="wp-block-code language-sql"><code>"FROM customers WHERE email = '" + email + "'"</code></pre>



<p class="wp-block-paragraph">Uygulama, <code>email</code> değişkeninin içeriğini SQL komutunun bir parçası hâline getiriyor.</p>



<p class="wp-block-paragraph">Geliştirici burada SQL&#8217;in nerede bittiğini ve kullanıcı verisinin nerede başladığını veritabanına güvenilir şekilde anlatmıyor.</p>



<h3 class="wp-block-heading">Saldırgan Sorguyu Nasıl Değiştirir?</h3>



<p class="wp-block-paragraph">Saldırganın e-posta alanına şu ifadeyi yazdığını düşünelim:</p>



<pre class="wp-block-code language-sql"><code>' OR '1'='1'</code></pre>



<p class="wp-block-paragraph">Uygulama bu ifadeyi sorguya doğrudan eklerse ortaya şu SQL çıkar:</p>



<pre class="wp-block-code language-sql"><code>SELECT id, email, full_name
FROM customers
WHERE email = '' OR '1'='1';</code></pre>



<p class="wp-block-paragraph">Sorgunun mantığı artık değişti.</p>



<p class="wp-block-paragraph"><code>'1'='1'</code> ifadesi her zaman doğru sonuç verir. Dolayısıyla <code>WHERE</code> koşulu bütün satırlar için doğru olur.</p>



<p class="wp-block-paragraph">Veritabanı sorgusu tek bir müşteri yerine birden fazla müşteriyi döndürebilir.</p>



<p class="wp-block-paragraph">Örnekteki Java metodu yalnızca ilk kaydı okuyacak olsa da saldırgan arama sonucunun kapsamını değiştirmiş olur. Uygulamanın geri kalan mantığı bu davranışı hesap erişimi veya yetkilendirme için kullanıyorsa daha ciddi sorunlar ortaya çıkabilir.</p>



<p class="wp-block-paragraph">Bu örnek, SQL Injection&#8217;ın temel mekanizmasını gösterir: <strong>Saldırgan veri göndermek yerine sorgunun mantığını değiştirir.</strong></p>



<h2 class="wp-block-heading">SQL Injection Neden Tehlikelidir?</h2>



<p class="wp-block-paragraph">Saldırının etkisini yalnızca sorgu sonucunda kaç kayıt gördüğümüzle ölçemeyiz.</p>



<p class="wp-block-paragraph">Veritabanı kullanıcısının yetkileri, uygulamanın işlevleri ve verinin niteliği de sonuçları belirler.</p>



<h3 class="wp-block-heading">Veri Gizliliğinin Kaybı</h3>



<p class="wp-block-paragraph">Bir uygulama müşteri adreslerini, e-posta bilgilerini veya finansal kayıtları saklıyor olabilir.</p>



<p class="wp-block-paragraph">SQL Injection açığı, saldırganın bu verilere yetkisiz erişim elde etmesine yol açabilir.</p>



<p class="wp-block-paragraph">Güvenlik açısından sorun yalnızca verinin dışarı çıkması değildir. Uygulamanın normalde göstermediği bilgilerin başka bir kullanıcıya ulaşması da gizlilik ihlalidir.</p>



<h3 class="wp-block-heading">Veri Bütünlüğünün Bozulması</h3>



<p class="wp-block-paragraph">Yeterli veritabanı yetkisine sahip bir uygulama üzerinden saldırgan bazı kayıtları değiştirebilir veya silebilir.</p>



<p class="wp-block-paragraph">Örneğin ürün fiyatları, sipariş durumları ve müşteri hesap bilgileri saldırgan için hedef oluşturabilir.</p>



<p class="wp-block-paragraph">Bu tür değişiklikler yalnızca teknik hata yaratmaz. İş süreçlerini ve raporların doğruluğunu da etkiler.</p>



<h3 class="wp-block-heading">Hizmet Kesintileri</h3>



<p class="wp-block-paragraph">Bazı SQL sorguları çok fazla işlemci, bellek veya disk kaynağı tüketebilir.</p>



<p class="wp-block-paragraph">Saldırganın sorgu davranışını değiştirmesi, veritabanının performansını düşürebilir. Ağır durumlarda uygulama hizmet veremez hâle gelebilir.</p>



<h3 class="wp-block-heading">Yetkilendirme Sorunları</h3>



<p class="wp-block-paragraph">SQL Injection, kimlik doğrulama ve yetkilendirme mekanizmalarını etkileyebilir.</p>



<p class="wp-block-paragraph">Ancak her SQL Injection açığı otomatik olarak giriş ekranını aşma imkânı vermez.</p>



<p class="wp-block-paragraph">Saldırganın hangi işlemleri gerçekleştirebileceğini sorgunun konumu, uygulama mantığı ve veritabanı yetkileri belirler.</p>



<h2 class="wp-block-heading">SQL Injection Türleri</h2>



<p class="wp-block-paragraph">Güvenlik uzmanları SQL Injection saldırılarını çoğunlukla uygulamanın veritabanından nasıl yanıt aldığına göre sınıflandırır.</p>



<h3 class="wp-block-heading">1. In-Band SQL Injection</h3>



<p class="wp-block-paragraph">Bu yöntemde saldırgan aynı iletişim kanalı üzerinden hem sorguyu etkiler hem de sonucu görür.</p>



<p class="wp-block-paragraph">İki yaygın alt türü bulunur.</p>



<p class="wp-block-paragraph"><strong>Error-Based SQL Injection</strong></p>



<p class="wp-block-paragraph">Saldırgan, veritabanının ürettiği hata mesajlarından teknik bilgi elde etmeye çalışır.</p>



<p class="wp-block-paragraph">Hata mesajları bazen tablo isimleri, sütunlar veya SQL sözdizimi hakkında ipuçları verebilir.</p>



<p class="wp-block-paragraph"><strong>Union-Based SQL Injection</strong></p>



<p class="wp-block-paragraph">Saldırgan, uygun koşullarda <code>UNION</code> operatörünü kullanarak farklı sorguların sonuçlarını birleştirmeye çalışır.</p>



<p class="wp-block-paragraph">Bu yöntem belirli sorgu yapısı ve veri tipi uyumluluğu koşullarını gerektirir.</p>



<h3 class="wp-block-heading">2. Blind SQL Injection</h3>



<p class="wp-block-paragraph">Bazı uygulamalar sorgu sonucunu veya veritabanı hatalarını doğrudan göstermez.</p>



<p class="wp-block-paragraph">Bu durum SQL Injection riskini ortadan kaldırmaz.</p>



<p class="wp-block-paragraph">Saldırgan, uygulamanın davranışındaki küçük farkları gözlemleyerek bilgi elde etmeye çalışabilir.</p>



<p class="wp-block-paragraph"><strong>Boolean-Based Blind SQL Injection</strong></p>



<p class="wp-block-paragraph">Saldırgan doğru ve yanlış koşullara karşılık uygulamanın farklı tepkiler verip vermediğini inceler.</p>



<p class="wp-block-paragraph"><strong>Time-Based Blind SQL Injection</strong></p>



<p class="wp-block-paragraph">Saldırgan veritabanının yanıt süresindeki farklılıklardan çıkarım yapmaya çalışır.</p>



<p class="wp-block-paragraph">Ancak ağ gecikmeleri ve sistem yükü, zaman ölçümlerini yanıltabilir.</p>



<h3 class="wp-block-heading">3. Out-of-Band SQL Injection</h3>



<p class="wp-block-paragraph">Bazı senaryolarda saldırgan, veritabanının farklı bir iletişim kanalı üzerinden dış sistemlerle etkileşim kurmasını hedefler.</p>



<p class="wp-block-paragraph">Bu yaklaşım özel veritabanı özellikleri, yapılandırmalar ve ağ erişim koşulları gerektirir.</p>



<h3 class="wp-block-heading">4. Second-Order SQL Injection</h3>



<p class="wp-block-paragraph">Bu türü özellikle gözden kaçırmak kolaydır.</p>



<p class="wp-block-paragraph">Uygulama saldırganın girdisini önce veritabanına kaydeder. Daha sonra başka bir işlem bu veriyi okuyup güvensiz biçimde yeni bir SQL sorgusuna ekler.</p>



<p class="wp-block-paragraph">Dolayısıyla güvenlik açığı ilk veri girişinde değil, verinin sonraki kullanımında ortaya çıkar.</p>



<p class="wp-block-paragraph">Bu nedenle geliştirici yalnızca HTTP isteklerini değil, veritabanından, dosyalardan ve diğer servislerden gelen verileri de sorgu oluştururken güvenilmeyen girdi olarak ele almalıdır.</p>



<h2 class="wp-block-heading">SQL Injection Nasıl Önlenir?</h2>



<p class="wp-block-paragraph">SQL Injection&#8217;a karşı en güçlü temel savunma, SQL komutlarının yapısını kullanıcı verilerinden ayrı tutmaktır.</p>



<p class="wp-block-paragraph">OWASP, veri değerleri için parametreli sorguları temel savunma olarak önerir.</p>



<h3 class="wp-block-heading">1. PreparedStatement ve Parametreli Sorgular Kullanın</h3>



<p class="wp-block-paragraph">Java JDBC API, <code>PreparedStatement</code> sınıfıyla parametreli sorgular oluşturmanıza imkân verir.</p>



<p class="wp-block-paragraph">Önceki güvensiz örneği yeniden yazalım.</p>



<pre class="wp-block-code language-java"><code>public Customer findByEmail(
        Connection connection,
        String email
) throws SQLException {

    String sql = """
        SELECT id, email, full_name
        FROM customers
        WHERE email = ?
        """;

    try (PreparedStatement statement =
             connection.prepareStatement(sql)) {

        statement.setString(1, email);

        try (ResultSet rs =
                 statement.executeQuery()) {

            if (rs.next()) {
                return new Customer(
                    rs.getLong("id"),
                    rs.getString("email"),
                    rs.getString("full_name")
                );
            }

            return null;
        }
    }
}</code></pre>



<p class="wp-block-paragraph">Burada iki önemli değişiklik yaptık.</p>



<p class="wp-block-paragraph">İlk olarak kullanıcı girdisini SQL metnine eklemek yerine <code>?</code> yer tutucusunu kullandık.</p>



<p class="wp-block-paragraph">İkinci olarak <code>setString()</code> metoduyla parametre değerini sorguya bağladık.</p>



<p class="wp-block-paragraph">JDBC sürücüsü, parametre bağlama mekanizması üzerinden SQL yapısıyla veri değerini ayrı ele alır.</p>



<p class="wp-block-paragraph">Artık kullanıcı şu ifadeyi gönderse bile:</p>



<pre class="wp-block-code language-sql"><code>' OR '1'='1'</code></pre>



<p class="wp-block-paragraph">Veritabanı bunu sorgunun mantığını değiştiren bir SQL ifadesi olarak değil, <code>email</code> sütununda aranacak bir değer olarak değerlendirir.</p>



<p class="wp-block-paragraph"><strong>PreparedStatement neden güvenlidir?</strong></p>



<p class="wp-block-paragraph">Çünkü SQL&#8217;in komut yapısı ile dışarıdan gelen veri arasında açık bir sınır kurar.</p>



<p class="wp-block-paragraph">Tek başına kullanıcı girdisindeki özel karakterleri yasaklamamız gerekmez.</p>



<p class="wp-block-paragraph">Örneğin <code>O'Connor</code> gibi tek tırnak içeren gerçek isimler de güvenli biçimde işlenebilir.</p>



<p class="wp-block-paragraph">Ancak geliştirici SQL yapısını kullanıcı girdisiyle birleştirmeye devam ederse yalnızca sorgunun başka bir bölümünde PreparedStatement kullanması yeterli olmaz.</p>



<h3 class="wp-block-heading">2. Dinamik SQL Yapıları İçin Allowlist Kullanın</h3>



<p class="wp-block-paragraph">Parametreli sorguların önemli bir sınırı bulunur.</p>



<p class="wp-block-paragraph"><code>?</code> yer tutucusu tablo ismi, sütun ismi veya <code>ASC</code> ve <code>DESC</code> gibi SQL anahtar kelimeleri yerine geçmez.</p>



<p class="wp-block-paragraph">Bir e-ticaret uygulamasında kullanıcılar ürünleri fiyat veya isim üzerinden sıralamak isteyebilir.</p>



<p class="wp-block-paragraph">Şu yaklaşım güvenli değildir:</p>



<pre class="wp-block-code language-sql"><code>String sql =
    "SELECT id, name, price FROM products ORDER BY "
    + sortBy;</code></pre>



<p class="wp-block-paragraph">Çünkü uygulama dışarıdan gelen <code>sortBy</code> değerini doğrudan SQL yapısına ekler.</p>



<p class="wp-block-paragraph">Bunun yerine izin verdiğimiz seçenekleri kodda tanımlayalım.</p>



<pre class="wp-block-code language-java"><code>public String createProductQuery(
        String sortBy,
        String direction
) {
    String column = switch (sortBy) {
        case "price" -&gt; "price";
        case "name"  -&gt; "name";
        default -&gt; throw new
            IllegalArgumentException(
                "Unsupported sort field"
            );
    };

    String order = switch (direction) {
        case "asc"  -&gt; "ASC";
        case "desc" -&gt; "DESC";
        default -&gt; throw new
            IllegalArgumentException(
                "Unsupported direction"
            );
    };

    return """
        SELECT id, name, price
        FROM products
        ORDER BY %s %s
        """.formatted(column, order);
}</code></pre>



<p class="wp-block-paragraph">Burada kullanıcı yalnızca önceden tanımladığımız seçenekler arasından seçim yapabilir.</p>



<p class="wp-block-paragraph">Uygulama SQL sorgusuna doğrudan kullanıcı girdisini değil, kendi belirlediği güvenilir SQL ifadelerini ekler.</p>



<p class="wp-block-paragraph">Dinamik yapıları bu şekilde sınırlandırırken diğer veri değerleri için yine parametreli sorgular kullanmalıyız.</p>



<h3 class="wp-block-heading">3. Girdi Doğrulama Uygulayın</h3>



<p class="wp-block-paragraph">Bir e-posta alanı e-posta biçimini, bir ürün kimliği ise beklenen veri tipini taşımalıdır.</p>



<p class="wp-block-paragraph">Girdi doğrulama uygulamanın beklenmedik verilerle karşılaşmasını azaltır.</p>



<p class="wp-block-paragraph">Ancak doğrulama SQL Injection&#8217;a karşı tek başına yeterli değildir.</p>



<p class="wp-block-paragraph">Örneğin sadece tek tırnak karakterini engellemek güvenilir bir savunma oluşturmaz.</p>



<p class="wp-block-paragraph">Farklı SQL sözdizimleri, karakter dönüşümleri ve sorgu bağlamları bu tür filtreleri yetersiz bırakabilir.</p>



<p class="wp-block-paragraph">Doğru yaklaşım şudur:</p>



<ul class="wp-block-list">
<li>Girdi doğrulamayı iş kuralları için kullanın.</li>



<li>Parametreli sorgularla SQL yapısını koruyun.</li>



<li>Dinamik SQL parçalarını allowlist ile sınırlandırın.</li>
</ul>



<h3 class="wp-block-heading">4. Veritabanında En Az Ayrıcalık İlkesini Uygulayın</h3>



<p class="wp-block-paragraph">Bir uygulamanın yalnızca müşteri listesini okuması gerekiyorsa veritabanı hesabına tüm tabloları değiştirme yetkisi vermemelisiniz.</p>



<p class="wp-block-paragraph">Örneğin PostgreSQL üzerinde yalnızca ürün okuma görevi olan bir hesap için gerekli erişimi sınırlayabilirsiniz.</p>



<pre class="wp-block-code language-sql"><code>REVOKE ALL ON TABLE products
FROM product_reader;

GRANT SELECT ON TABLE products
TO product_reader;</code></pre>



<p class="wp-block-paragraph">Bu örnek ilgili tablo üzerindeki yetkileri sınırlar. Gerçek sistemde rol üyeliklerini, şema erişimini, varsayılan yetkileri ve diğer tabloları da kontrol etmelisiniz.</p>



<p class="wp-block-paragraph">En az ayrıcalık ilkesi SQL Injection açığını doğrudan engellemez.</p>



<p class="wp-block-paragraph">Ancak saldırgan bir açık bulsa bile veritabanı hesabının yapabileceği işlemleri sınırlar.</p>



<h3 class="wp-block-heading">5. ORM Kullanırken Dinamik Sorgulara Dikkat Edin</h3>



<p class="wp-block-paragraph">Hibernate veya JPA kullanmak SQL Injection riskini otomatik olarak ortadan kaldırmaz.</p>



<p class="wp-block-paragraph">Örneğin şu JPQL oluşturma biçimi risklidir:</p>



<pre class="wp-block-code language-sql"><code>String jpql =
    "SELECT u FROM User u WHERE u.email = '"
    + email + "'";

var query = entityManager.createQuery(
    jpql,
    User.class
);</code></pre>



<p class="wp-block-paragraph">Güvenli yaklaşımda parametre bağlamalıyız:</p>



<pre class="wp-block-code language-java"><code>var query = entityManager.createQuery(
    """
    SELECT u
    FROM User u
    WHERE u.email = :email
    """,
    User.class
);

query.setParameter("email", email);

List&lt;User&gt; users = query.getResultList();</code></pre>



<p class="wp-block-paragraph">ORM araçları geliştiricinin işini kolaylaştırır. Ancak sorgu oluşturma kararının güvenliği yine geliştiricinin sorumluluğundadır.</p>



<p class="wp-block-paragraph">Özellikle native SQL, dinamik JPQL/HQL ve elle oluşturduğunuz sıralama koşullarını dikkatle incelemelisiniz.</p>



<h3 class="wp-block-heading">6. Stored Procedure Kullanımını da Denetleyin</h3>



<p class="wp-block-paragraph">Stored procedure kullanımı SQL Injection riskini tek başına ortadan kaldırmaz.</p>



<p class="wp-block-paragraph">Bir procedure içinde kullanıcı verilerini birleştirerek dinamik SQL oluşturursanız aynı güvenlik açığını yeniden yaratabilirsiniz.</p>



<p class="wp-block-paragraph">Procedure çağrısında parametre bağlamak önemlidir. Ancak procedure içindeki dinamik sorgular için de aynı güvenlik kuralını uygulamalısınız.</p>



<p class="wp-block-paragraph">Statik SQL tercih edin. Dinamik SQL gerektiğinde veri parametrelerini bağlayın ve SQL yapılarını güvenilir seçeneklerle sınırlandırın.</p>



<h3 class="wp-block-heading">7. Hata Mesajlarını ve Kayıtları Kontrol Edin</h3>



<p class="wp-block-paragraph">Geliştirme sırasında ayrıntılı SQL hataları sorunları çözmenize yardımcı olur.</p>



<p class="wp-block-paragraph">Ancak canlı ortamda ham veritabanı hatalarını istemciye göndermek gereksiz bilgi açığa çıkarabilir.</p>



<p class="wp-block-paragraph">Uygulama, kullanıcıya genel bir hata yanıtı vermeli ve teknik ayrıntıları yetkili ekiplerin erişebileceği güvenli log sisteminde tutmalıdır.</p>



<p class="wp-block-paragraph">Log kayıtlarına parola, erişim belirteci veya hassas kişisel veri eklemeyin.</p>



<p class="wp-block-paragraph">Ayrıca veritabanı kullanıcısının izinlerini ve uygulamanın hata yönetimini düzenli olarak inceleyin.</p>



<h2 class="wp-block-heading">SQL Injection Açıklarını Nasıl Test Edebilirsiniz?</h2>



<p class="wp-block-paragraph">SQL Injection kontrollerini yalnızca sahibi olduğunuz veya açıkça test izni aldığınız sistemlerde gerçekleştirin.</p>



<p class="wp-block-paragraph">Güvenli bir geliştirme ortamında aşağıdaki yaklaşımı kullanabilirsiniz.</p>



<h3 class="wp-block-heading">Kod İncelemesi</h3>



<p class="wp-block-paragraph">Öncelikle SQL sorgularını oluşturan kodu inceleyin.</p>



<p class="wp-block-paragraph">Özellikle şu kalıplara dikkat edin:</p>



<ul class="wp-block-list">
<li>SQL metniyle kullanıcı girdisinin birleştirilmesi</li>



<li><code>Statement</code> ile dinamik sorgu çalıştırma</li>



<li>Native SQL ve dinamik JPQL/HQL oluşturma</li>



<li>Stored procedure içinde dinamik SQL kullanımı</li>



<li><code>ORDER BY</code> ve tablo isimleri gibi yapısal alanlarda kullanıcı girdisi kullanımı</li>
</ul>



<p class="wp-block-paragraph">Her <code>Statement</code> kullanımı otomatik olarak açık anlamına gelmez. Asıl risk, güvenilmeyen girdinin SQL komutunun yapısını değiştirebilmesidir.</p>



<h3 class="wp-block-heading">Otomatik Güvenlik Testleri</h3>



<p class="wp-block-paragraph">Statik uygulama güvenliği testleri (SAST), kaynak kodda şüpheli sorgu oluşturma kalıplarını bulmanıza yardımcı olabilir.</p>



<p class="wp-block-paragraph">Dinamik uygulama güvenliği testleri (DAST), çalışan uygulamanın girdilere verdiği tepkileri analiz eder.</p>



<p class="wp-block-paragraph">OWASP ZAP ve uygun yapılandırılmış güvenlik test araçları, yetkili test süreçlerinde yardımcı olabilir.</p>



<p class="wp-block-paragraph">Bir aracın herhangi bir bulgu üretmemesi uygulamanın kesinlikle güvenli olduğu anlamına gelmez.</p>



<h3 class="wp-block-heading">Java ile Basit Bir Regresyon Testi</h3>



<p class="wp-block-paragraph">Parametreli sorgunun beklendiği gibi davrandığını birim veya entegrasyon testinde de kontrol edebilirsiniz.</p>



<p class="wp-block-paragraph">Örneğin aşağıdaki JUnit 5 testi, depo katmanının şüpheli bir girdiyi geçerli müşteri kaydı gibi yorumlamamasını bekler.</p>



<pre class="wp-block-code language-java"><code>@Test
void shouldTreatSqlPayloadAsPlainData()
        throws SQLException {

    String input = "' OR '1'='1";

    Customer result =
        repository.findByEmail(
            connection,
            input
        );

    assertNull(result);
}</code></pre>



<p class="wp-block-paragraph">Bu testi çalıştırırken izole test veritabanınızda ilgili girdiyi e-posta değeri olarak taşıyan bir kayıt bulunmadığından emin olun.</p>



<p class="wp-block-paragraph">Testin gerçek değerini artırmak için veritabanını başlangıç verileriyle kurun, bilinen bir müşteri kaydı ekleyin ve sorgunun beklenmedik kayıt döndürmediğini doğrulayın.</p>



<p class="wp-block-paragraph">Tek bir test bütün SQL Injection türlerini kapsamaz. Ancak daha önce düzelttiğiniz bir açığın tekrar ortaya çıkmasını yakalayabilir.</p>



<h2 class="wp-block-heading">Bir E-Ticaret Uygulamasında SQL Injection Güvenliği</h2>



<p class="wp-block-paragraph">Bir e-ticaret sisteminin yalnızca giriş ekranını koruduğunu varsayalım.</p>



<p class="wp-block-paragraph">Bu yeterli değildir.</p>



<p class="wp-block-paragraph">Ürün arama, fiyat filtreleme, müşteri hesapları, sipariş geçmişi, kampanya sorguları ve yönetim paneli aynı güvenlik prensiplerine ihtiyaç duyar.</p>



<p class="wp-block-paragraph">Örneğin ürün arama sorgusunu şöyle oluşturabiliriz:</p>



<pre class="wp-block-code language-java"><code>public List&lt;Product&gt; searchProducts(
        Connection connection,
        String keyword,
        BigDecimal maxPrice
) throws SQLException {

    String sql = """
        SELECT id, name, price
        FROM products
        WHERE LOWER(name) LIKE LOWER(?)
          AND price &lt;= ?
        """;

    List&lt;Product&gt; products =
        new ArrayList&lt;&gt;();

    try (PreparedStatement statement =
             connection.prepareStatement(sql)) {

        statement.setString(
            1,
            "%" + keyword + "%"
        );

        statement.setBigDecimal(
            2,
            maxPrice
        );

        try (ResultSet rs =
                 statement.executeQuery()) {

            while (rs.next()) {
                products.add(
                    new Product(
                        rs.getLong("id"),
                        rs.getString("name"),
                        rs.getBigDecimal("price")
                    )
                );
            }
        }
    }

    return products;
}</code></pre>



<p class="wp-block-paragraph">Bu örnekte iki farklı parametre bağladık.</p>



<p class="wp-block-paragraph">İlk parametre ürün isminde arama yapıyor. İkinci parametre maksimum fiyatı belirliyor.</p>



<p class="wp-block-paragraph"><code>keyword</code> değerini Java tarafında <code>%</code> karakterleriyle birleştiriyoruz. Ancak bu birleşim SQL sorgusunun yapısını değil, parametre değerini değiştiriyor.</p>



<p class="wp-block-paragraph">Bu nedenle sorgu SQL Injection&#8217;a karşı güvenli bir sınır koruyor.</p>



<p class="wp-block-paragraph">Burada küçük bir ayrıntı daha var: Kullanıcı <code>%</code> veya <code>_</code> karakterlerini arama metnine eklerse <code>LIKE</code> operatörü bu karakterlere özel anlam yükleyebilir.</p>



<p class="wp-block-paragraph">Bu davranış SQL Injection değildir. Ancak uygulama düz metin araması bekliyorsa geliştirici joker karakterleri ayrıca ele almalıdır.</p>



<p class="wp-block-paragraph">Güvenli yazılım geliştirme yalnızca SQL Injection&#8217;ı durdurmakla bitmez. Uygulamanın iş kurallarını doğru uygulamasını da gerektirir.</p>



<h2 class="wp-block-heading">SQL Injection Hakkında Sık Sorulan Sorular</h2>



<h3 class="wp-block-heading">SQL Injection sadece giriş ekranlarında mı ortaya çıkar?</h3>



<p class="wp-block-paragraph">Hayır. Arama alanları, filtreler, raporlama ekranları, API parametreleri ve veritabanına sorgu gönderen diğer işlemler SQL Injection riski taşıyabilir.</p>



<h3 class="wp-block-heading">PreparedStatement SQL Injection&#8217;ı tamamen engeller mi?</h3>



<p class="wp-block-paragraph">PreparedStatement, doğru parametre bağlama kullanımıyla sorgunun veri değerlerinden kaynaklanan SQL Injection riskini önler. Ancak kullanıcı girdisini SQL yapısına başka bir noktada eklemek yeni bir açık oluşturabilir.</p>



<h3 class="wp-block-heading">Hibernate veya JPA kullanırsam güvende olur muyum?</h3>



<p class="wp-block-paragraph">ORM araçları parametreli sorgu kullanımını kolaylaştırır. Ancak dinamik sorguları güvensiz biçimde oluşturursanız SQL Injection açığı yine ortaya çıkabilir.</p>



<h3 class="wp-block-heading">SQL Injection ve XSS aynı şey mi?</h3>



<p class="wp-block-paragraph">Hayır. SQL Injection veritabanı sorgularının yapısını hedefler. Cross-Site Scripting (XSS) ise tarayıcının işlediği içerik üzerinden istemci tarafındaki güven sınırlarını hedefler.</p>



<h3 class="wp-block-heading">SQL Injection önlemek için kullanıcı girdisindeki özel karakterleri engellemeli miyim?</h3>



<p class="wp-block-paragraph">Hayır. Genel bir özel karakter yasağı güvenilir çözüm sunmaz ve geçerli kullanıcı verilerini de reddedebilir. Parametreli sorgular, iş kurallarına uygun doğrulama ve allowlist yaklaşımı daha doğru bir savunma oluşturur.</p>



<h3 class="wp-block-heading">Web Application Firewall (WAF) yeterli mi?</h3>



<p class="wp-block-paragraph">WAF bazı şüpheli istekleri engelleyebilir. Ancak uygulamanın güvensiz sorgu oluşturma hatasını düzeltmez. WAF&#8217;ı ek koruma katmanı olarak değerlendirin.</p>



<h2 class="wp-block-heading">Sonuç: SQL Injection&#8217;a Karşı Güvenli Kodlama Alışkanlığı</h2>



<p class="wp-block-paragraph">SQL Injection, uygulamanın veri ile komut arasındaki sınırı kaybetmesiyle ortaya çıkar.</p>



<p class="wp-block-paragraph">Geliştirici kullanıcı girdisini SQL sorgusuna doğrudan eklediğinde saldırgan sorgunun davranışını etkileyebilir.</p>



<p class="wp-block-paragraph">Bu riski azaltmak için karmaşık güvenlik algoritmalarına ihtiyacımız yok. Öncelikle doğru sorgu oluşturma alışkanlığını benimsememiz gerekiyor.</p>



<p class="wp-block-paragraph">Java uygulamalarında <code>PreparedStatement</code> kullanmak, dinamik SQL parçalarını allowlist ile sınırlandırmak, veritabanı hesaplarının yetkilerini azaltmak ve sorguları düzenli test etmek güçlü bir başlangıç sağlar.</p>



<p class="wp-block-paragraph">En önemli kural oldukça basit:</p>



<p class="wp-block-paragraph"><strong>SQL komutlarını uygulama belirlesin. Kullanıcılar yalnızca veri sağlasın.</strong></p>



<p class="wp-block-paragraph">Bu ayrımı koruduğunuzda SQL Injection saldırılarının temel giriş noktasını kapatmış olursunuz.</p>



<h2 class="wp-block-heading">Kaynaklar</h2>



<ol start="1" class="wp-block-list">
<li>OWASP — SQL Injection Prevention Cheat Sheet<br><a href="https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html">https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html</a></li>



<li>OWASP — Top 10:2025, A05 Injection<br><a href="https://top10.owasp.org/2025/A05_2025-Injection/">https://top10.owasp.org/2025/A05_2025-Injection/</a></li>



<li>OWASP — Web Security Testing Guide<br><a href="https://owasp.org/www-project-web-security-testing-guide/">https://owasp.org/www-project-web-security-testing-guide/</a></li>



<li>Oracle — JDBC PreparedStatement<br><a href="https://docs.oracle.com/javase/tutorial/jdbc/basics/prepared.html">https://docs.oracle.com/javase/tutorial/jdbc/basics/prepared.html</a></li>
</ol>
<p><a href="https://alikarahisar.com/blog/sql-injection-nedir/">SQL Injection Nedir? Saldırıyı Anlamak ve Önlemek İçin Pratik Rehber</a> yazısı ilk önce <a href="https://alikarahisar.com/blog">Ali Karahisar</a> üzerinde ortaya çıktı.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://alikarahisar.com/blog/sql-injection-nedir/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Meet Markdrop: Convert Markdown to PDF and Word, with Local Images</title>
		<link>https://alikarahisar.com/blog/meet-markdrop-convert-markdown-to-pdf-and-word-with-local-images/</link>
					<comments>https://alikarahisar.com/blog/meet-markdrop-convert-markdown-to-pdf-and-word-with-local-images/#respond</comments>
		
		<dc:creator><![CDATA[Ali Karahisar]]></dc:creator>
		<pubDate>Sat, 17 Oct 2026 19:29:41 +0000</pubDate>
				<category><![CDATA[Araçlar]]></category>
		<category><![CDATA[Markdown]]></category>
		<category><![CDATA[Markdown Converter]]></category>
		<category><![CDATA[Markdown to PDF]]></category>
		<category><![CDATA[Markdown to Word]]></category>
		<guid isPermaLink="false">https://alikarahisar.com/blog/?p=96</guid>

					<description><![CDATA[<p>Your project notes are finished. The headings are clear, the checklist is complete, and the screenshots explain the details. There is just one more step: turning that Markdown file into a document someone else can review or share. Markdrop brings that step into your browser. It is a Markdown to PDF and Word converter with [&#8230;]</p>
<p><a href="https://alikarahisar.com/blog/meet-markdrop-convert-markdown-to-pdf-and-word-with-local-images/">Meet Markdrop: Convert Markdown to PDF and Word, with Local Images</a> yazısı ilk önce <a href="https://alikarahisar.com/blog">Ali Karahisar</a> üzerinde ortaya çıktı.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Your project notes are finished. The headings are clear, the checklist is complete, and the screenshots explain the details. There is just one more step: turning that Markdown file into a document someone else can review or share.</p>



<span id="more-96"></span>



<p class="wp-block-paragraph">Markdrop brings that step into your browser. It is a Markdown to PDF and Word converter with an editable source panel, a live A4 preview, and support for images you select from your computer. You can move from a plain-text draft to a PDF or an editable `.docx` file without creating an account or uploading your document to a conversion server.</p>



<h2 class="wp-block-heading">From a Markdown draft to a shareable document</h2>



<p class="wp-block-paragraph">Markdown is useful for keeping writing focused. A few characters create headings, lists, links, and tables, while the source stays easy to edit. When the next person needs a PDF attachment or a Word document, those same notes need a different form.</p>



<figure class="wp-block-image size-large"><img fetchpriority="high" decoding="async" width="1024" height="647" src="https://alikarahisar.com/blog/wp-content/uploads/2026/10/Screenshot-2026-10-17-at-22.14.13-1024x647.png" alt="markdrop_markdown_convert_main" class="wp-image-100" srcset="https://alikarahisar.com/blog/wp-content/uploads/2026/10/Screenshot-2026-10-17-at-22.14.13-1024x647.png 1024w, https://alikarahisar.com/blog/wp-content/uploads/2026/10/Screenshot-2026-10-17-at-22.14.13-300x190.png 300w, https://alikarahisar.com/blog/wp-content/uploads/2026/10/Screenshot-2026-10-17-at-22.14.13-768x485.png 768w, https://alikarahisar.com/blog/wp-content/uploads/2026/10/Screenshot-2026-10-17-at-22.14.13-1536x970.png 1536w, https://alikarahisar.com/blog/wp-content/uploads/2026/10/Screenshot-2026-10-17-at-22.14.13-2048x1294.png 2048w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">With Markdrop, you can open a `.md` or `.markdown` file, edit its source, and see a formatted document beside it. The preview uses an A4 layout with readable typography for headings, paragraphs, tables, quotations, and code blocks.</p>



<p class="wp-block-paragraph">That makes it useful for everyday handoffs: a project proposal with a diagram, meeting notes with action items, documentation for a colleague, or a brief that needs another round of editing in Word.</p>



<h2 class="wp-block-heading">How to convert Markdown with Markdrop</h2>



<h3 class="wp-block-heading"><strong>1. Bring in your file or start writing</strong></h3>



<p class="wp-block-paragraph">Drag a Markdown file into the workspace, choose <strong>browse files</strong>, or type directly into the editor. Markdrop accepts Markdown files up to 10 MB.</p>



<p class="wp-block-paragraph">You can also paste a copied file when your browser and operating system make it available through the clipboard. If the clipboard contains only a filename or a path, choose the file through the browser instead. Ordinary text can be pasted straight into the editor.</p>



<h3 class="wp-block-heading">2. Add any local images</h3>



<p class="wp-block-paragraph">A Markdown image reference usually points to a separate file:</p>



<pre class="wp-block-code language-markdown"><code>```markdown
!&#91;Project overview](images/overview.png)
```</code></pre>



<p class="wp-block-paragraph">The Markdown document contains the path `images/overview.png`, not the image itself. Selecting the document does not give your browser permission to read that image from your computer.</p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="675" src="https://alikarahisar.com/blog/wp-content/uploads/2026/10/Screenshot-2026-10-17-at-22.06.07-1024x675.png" alt="markdrop_markdown_convert_preview" class="wp-image-102" srcset="https://alikarahisar.com/blog/wp-content/uploads/2026/10/Screenshot-2026-10-17-at-22.06.07-1024x675.png 1024w, https://alikarahisar.com/blog/wp-content/uploads/2026/10/Screenshot-2026-10-17-at-22.06.07-300x198.png 300w, https://alikarahisar.com/blog/wp-content/uploads/2026/10/Screenshot-2026-10-17-at-22.06.07-767x506.png 767w, https://alikarahisar.com/blog/wp-content/uploads/2026/10/Screenshot-2026-10-17-at-22.06.07-1536x1013.png 1536w, https://alikarahisar.com/blog/wp-content/uploads/2026/10/Screenshot-2026-10-17-at-22.06.07-2048x1351.png 2048w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Use <strong>Add images</strong> to select it, or drop one Markdown file together with its images. Markdrop supports PNG, JPEG, and still WebP files and prepares them locally for the preview and both export formats.</p>



<h3 class="wp-block-heading">3. Review the preview and document checks</h3>



<p class="wp-block-paragraph">Edit your Markdown on the left and review the document on the right. Headings, emphasis, lists, tables, and supported images update as you work.</p>



<p class="wp-block-paragraph">The <strong>Document checks</strong> section calls out issues that could affect the result, such as a missing image or an ambiguous filename. Resolve those issues before exporting, or explicitly choose to include visible placeholders for unavailable images.</p>



<h3 class="wp-block-heading">4. Download a PDF or Word document</h3>



<p class="wp-block-paragraph">Choose <strong>Export to PDF</strong> for a rendered document or <strong>Export to Word (.docx)</strong> for editable text with embedded images. Downloads use your document’s filename, which you can also edit in the workspace.</p>



<p class="wp-block-paragraph">For example, `project-brief.md` becomes `project-brief.pdf` or `project-brief.docx`.</p>



<h2 class="wp-block-heading"><strong>Local images, with clear path matching</strong></h2>



<p class="wp-block-paragraph">Image folders often contain familiar filenames: `logo.png`, `screenshot.png`, or `diagram.webp`. Markdrop can match a unique filename automatically, but it also lets you enter the exact Markdown path for each selected image.</p>



<p class="wp-block-paragraph">Suppose your document uses both of these references:</p>



<pre class="wp-block-code language-markdown"><code>```markdown
!&#91;Primary logo](brand/logo.png)

!&#91;Footer logo](footer/logo.png)

```</code></pre>



<p class="wp-block-paragraph">Those paths may point to different images. Markdrop asks you to resolve the mapping instead of guessing which logo belongs in each position. Set the paths beside the selected images to `brand/logo.png` and `footer/logo.png`, and the preview updates accordingly.</p>



<p class="wp-block-paragraph">Paths with spaces also work with standard Markdown syntax:</p>



<pre class="wp-block-code language-markdown"><code>```markdown

!&#91;Workshop notes](&lt;images/workshop photo.jpg>)

```</code></pre>



<p class="wp-block-paragraph">Matching is case-sensitive. A reference to `Photo.jpg` is different from one to `photo.jpg`, so the workspace makes path checks part of the conversion process.</p>



<h2 class="wp-block-heading">Your document is processed on your device</h2>



<p class="wp-block-paragraph">File reading, Markdown parsing, image preparation, and document generation happen in the browser. Markdrop does not upload your document or selected images, and the application does not include analytics or persistent document storage.</p>



<p class="wp-block-paragraph">It does need an internet connection to load its third-party libraries from CDNs. Those library requests do not include your document contents, but they mean Markdrop should not be described as a fully offline application.</p>



<p class="wp-block-paragraph">Remote image URLs do not load automatically. To include an image from a website, save an appropriate local copy, select it through <strong>Add images</strong>, and update the Markdown reference. This also keeps document rendering from silently contacting image hosts.</p>



<p class="wp-block-paragraph">Selected images are prepared as PNG files with the original image metadata removed. Their longest side is limited to 2,400 pixels for export, so keep your originals when full-resolution images matter.</p>



<h2 class="wp-block-heading">Choose the output that suits the next step</h2>



<p class="wp-block-paragraph">Word is useful when the recipient needs to revise the document. Markdrop exports editable text, native lists and tables, and embedded images. Markdown image descriptions are also carried into the Word file.</p>



<p class="wp-block-paragraph">PDF is useful for a rendered version of the document, with an important distinction: Markdrop’s PDF export uses page images. Its text is not selectable, and the resulting PDF is not tagged for accessibility. If searchable text or accessible publishing is a requirement, choose the Word output and use a suitable document workflow afterward.</p>



<p class="wp-block-paragraph">The A4 preview is a continuous sheet. Page breaks, table widths, and typography can differ in the exported files, so review the result before sharing it. The browser’s <strong>Print</strong> option is also available, including as an alternative for documents that exceed the PDF renderer’s size limits.</p>



<h2 class="wp-block-heading">A few details to check before you export</h2>



<p class="wp-block-paragraph">Markdrop supports up to 20 selected images, with a 10 MB limit per image and a 40 MB combined limit for both the original image collection and the prepared PNG collection. Large dimensions, damaged files, and unsupported formats produce explanatory messages. SVG, GIF, HEIC, TIFF, and animated PNG or WebP files need conversion to a supported still image first.</p>



<p class="wp-block-paragraph">Some Markdown extensions also need attention. Mermaid diagrams and math code blocks remain source code; they are not rendered as diagrams or equations. You can include a prepared image when a visual result is needed. YAML front matter is treated as document content, while local file links and internal anchors remain plain text.</p>



<p class="wp-block-paragraph">Your workspace lasts only for the current session. Reloading or closing the page clears the text and selected images, so keep your source files and export your work before leaving.</p>



<h2 class="wp-block-heading">Give your next draft a finished form</h2>



<p class="wp-block-paragraph">Start with a meeting note, a project brief, or a page of documentation. Add its images, check the preview, and choose the format that fits the person receiving it.</p>



<p class="wp-block-paragraph"><a href="https://alikarahisar.com/tools/markdrop/" target="_blank" rel="noreferrer noopener">Open Markdrop and convert your Markdown</a></p>
<p><a href="https://alikarahisar.com/blog/meet-markdrop-convert-markdown-to-pdf-and-word-with-local-images/">Meet Markdrop: Convert Markdown to PDF and Word, with Local Images</a> yazısı ilk önce <a href="https://alikarahisar.com/blog">Ali Karahisar</a> üzerinde ortaya çıktı.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://alikarahisar.com/blog/meet-markdrop-convert-markdown-to-pdf-and-word-with-local-images/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Java&#8217;da SOLID Prensipleri: Gerçek Kod Örnekleriyle 5 Temel İlke</title>
		<link>https://alikarahisar.com/blog/java-solid-prensipleri/</link>
					<comments>https://alikarahisar.com/blog/java-solid-prensipleri/#respond</comments>
		
		<dc:creator><![CDATA[Ali Karahisar]]></dc:creator>
		<pubDate>Sun, 11 Oct 2026 20:16:20 +0000</pubDate>
				<category><![CDATA[Design Patterns]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Yazılım]]></category>
		<category><![CDATA[Yazılım Mimarisi]]></category>
		<category><![CDATA[Tasarım Desenleri]]></category>
		<guid isPermaLink="false">https://alikarahisar.com/blog/?p=88</guid>

					<description><![CDATA[<p>Bir Java uygulamasının ilk sürümünü geliştirmek genellikle zor değildir. Asıl sorun, yeni özellikler geldikçe ortaya çıkar. Örneğin bir e-ticaret uygulamasında sipariş oluşturma işlemini düşünelim. İlk sürümde kullanıcı sipariş verir, sistem ödemeyi alır ve e-posta gönderir. Bir süre sonra ürün ekibi yeni ödeme yöntemleri, farklı indirim kuralları, SMS bildirimleri ve kurumsal müşterilere özel işlemler ister. Bu [&#8230;]</p>
<p><a href="https://alikarahisar.com/blog/java-solid-prensipleri/">Java&#8217;da SOLID Prensipleri: Gerçek Kod Örnekleriyle 5 Temel İlke</a> yazısı ilk önce <a href="https://alikarahisar.com/blog">Ali Karahisar</a> üzerinde ortaya çıktı.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Bir Java uygulamasının ilk sürümünü geliştirmek genellikle zor değildir. Asıl sorun, yeni özellikler geldikçe ortaya çıkar.</p>



<p class="wp-block-paragraph">Örneğin bir e-ticaret uygulamasında sipariş oluşturma işlemini düşünelim. İlk sürümde kullanıcı sipariş verir, sistem ödemeyi alır ve e-posta gönderir. Bir süre sonra ürün ekibi yeni ödeme yöntemleri, farklı indirim kuralları, SMS bildirimleri ve kurumsal müşterilere özel işlemler ister.</p>



<span id="more-88"></span>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="576" src="https://alikarahisar.com/blog/wp-content/uploads/2026/10/Javada-SOLID-Prensipleri-ve-E-Ticaret-Akisi-1024x576.png" alt="java_solid_prensipleri" class="wp-image-93" srcset="https://alikarahisar.com/blog/wp-content/uploads/2026/10/Javada-SOLID-Prensipleri-ve-E-Ticaret-Akisi-1024x576.png 1024w, https://alikarahisar.com/blog/wp-content/uploads/2026/10/Javada-SOLID-Prensipleri-ve-E-Ticaret-Akisi-300x169.png 300w, https://alikarahisar.com/blog/wp-content/uploads/2026/10/Javada-SOLID-Prensipleri-ve-E-Ticaret-Akisi-768x432.png 768w, https://alikarahisar.com/blog/wp-content/uploads/2026/10/Javada-SOLID-Prensipleri-ve-E-Ticaret-Akisi-1536x864.png 1536w, https://alikarahisar.com/blog/wp-content/uploads/2026/10/Javada-SOLID-Prensipleri-ve-E-Ticaret-Akisi.png 1672w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">SOLID ilkelerinin Java tabanlı sipariş yönetimi uygulamasındaki sorumluluk ve bağımlılık ilişkileri.</figcaption></figure>



<p class="wp-block-paragraph">Bu noktada geliştirici tek bir <code>OrderService</code> sınıfına sürekli yeni metotlar ekliyorsa basit bir değişiklik bile beklenmedik hatalara yol açabilir.</p>



<p class="wp-block-paragraph">SOLID prensipleri tam olarak bu tür tasarım sorunlarıyla ilgilenir.</p>



<p class="wp-block-paragraph"><strong>SOLID, nesne yönelimli yazılım tasarımında sorumlulukları ayırmayı, bağımlılıkları yönetmeyi ve değişikliklerin etkisini sınırlamayı hedefleyen beş temel prensibin baş harflerinden oluşur.</strong></p>



<p class="wp-block-paragraph">Bu makalede SOLID&#8217;in beş ilkesini gerçek Java örnekleriyle inceleyeceğiz. Her ilke için önce problemli yaklaşımı görecek, ardından kodu yeniden düzenleyeceğiz. Ayrıca Java 17 ve Java 21 özelliklerinin bu prensiplerle nasıl ilişkilendiğini ve gereksiz soyutlamalardan nasıl kaçınabileceğimizi ele alacağız.</p>



<h2 class="wp-block-heading">SOLID Prensipleri Nedir?</h2>



<p class="wp-block-paragraph">SOLID kavramı, Robert C. Martin&#8217;in yaygınlaştırdığı beş nesne yönelimli tasarım prensibini bir araya getirir. Michael Feathers bu prensiplerin baş harflerini SOLID kısaltması altında topladı.</p>



<p class="wp-block-paragraph">Bu ilkeler Java&#8217;ya özel değildir. C#, Kotlin, C++ ve nesne yönelimli başka dillerde de aynı tasarım problemleriyle karşılaşırız.</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><tbody><tr><th>Kısaltma</th><th>Prensip</th><th>Temel düşünce</th></tr><tr><td>S</td><td>Single Responsibility Principle</td><td>Bir modülün değişmesi için tek bir temel neden olmalı</td></tr><tr><td>O</td><td>Open/Closed Principle</td><td>Mevcut davranışı değiştirmeden yeni davranışlar eklemeyi kolaylaştır</td></tr><tr><td>L</td><td>Liskov Substitution Principle</td><td>Alt türler, üst türlerin sözleşmesini korumalı</td></tr><tr><td>I</td><td>Interface Segregation Principle</td><td>İstemcileri ihtiyaç duymadıkları metotlara bağımlı bırakma</td></tr><tr><td>D</td><td>Dependency Inversion Principle</td><td>Üst seviye iş kurallarını somut altyapı sınıflarına bağımlı kılma</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">Prensipleri birbirinden bağımsız kurallar gibi düşünmek doğru olmaz.</p>



<p class="wp-block-paragraph">Örneğin Dependency Inversion uyguladığınızda çoğu zaman Interface Segregation ilkesiyle de karşılaşırsınız. Strategy tasarım desenini kullanırken Open/Closed Principle önem kazanır. Sınıfların sorumluluklarını ayırdığınızda ise test yazmak kolaylaşır.</p>



<p class="wp-block-paragraph">Bununla birlikte SOLID&#8217;e uyan bir tasarımın otomatik olarak iyi tasarım olacağını söyleyemeyiz. Ekibin deneyimi, uygulamanın büyüklüğü ve iş gereksinimlerinin değişme sıklığı da mimari kararları etkiler.</p>



<h2 class="wp-block-heading">SOLID Prensipleri Neden Önemlidir?</h2>



<p class="wp-block-paragraph">Yazılım geliştirme sürecinde en yüksek maliyet her zaman ilk kodu yazmak değildir. Mevcut kodun nasıl çalıştığını anlamak, bir değişikliğin etkisini belirlemek ve yeni davranışı güvenli biçimde eklemek de zaman alır.</p>



<p class="wp-block-paragraph">SOLID bu maliyetleri azaltmaya yardımcı olur.</p>



<h3 class="wp-block-heading">Değişikliklerin Etkisini Sınırlar</h3>



<p class="wp-block-paragraph">Bir ödeme yöntemi eklemek için stok, bildirim ve raporlama kodlarını değiştirmek zorunda kalmamalısınız.</p>



<h3 class="wp-block-heading">Test Yazmayı Kolaylaştırır</h3>



<p class="wp-block-paragraph">Bir sınıf ihtiyaç duyduğu servisleri dışarıdan alıyorsa test sırasında gerçek veritabanı veya ödeme sistemi yerine kontrollü test nesneleri kullanabilirsiniz.</p>



<h3 class="wp-block-heading">Bağımlılıkları Daha Açık Hâle Getirir</h3>



<p class="wp-block-paragraph">Constructor üzerinden alınan bağımlılıklar, sınıfın hangi bileşenlere ihtiyaç duyduğunu doğrudan gösterir.</p>



<h3 class="wp-block-heading">Kodun Okunabilirliğini Artırabilir</h3>



<p class="wp-block-paragraph">Her sınıf belirli bir sorumluluğa odaklandığında geliştirici davranışın nerede bulunduğunu daha kolay bulur.</p>



<p class="wp-block-paragraph">Ancak aşırı soyutlama tam tersine etki edebilir. Her işlem için ayrı interface, factory, manager ve helper sınıfları oluşturmak kodun anlaşılmasını zorlaştırabilir.</p>



<p class="wp-block-paragraph">Önemli olan sınıf sayısını artırmak değil, değişikliklerin maliyetini düşürmektir.</p>



<h2 class="wp-block-heading">1. Single Responsibility Principle (SRP)</h2>



<p class="wp-block-paragraph">Single Responsibility Principle, Türkçede Tek Sorumluluk Prensibi olarak bilinir.</p>



<p class="wp-block-paragraph">Bu ilkeyi genellikle &#8220;Bir sınıf yalnızca tek bir iş yapmalı&#8221; şeklinde duyarız. Ancak bu açıklama eksik kalır.</p>



<p class="wp-block-paragraph">SRP&#8217;nin daha anlamlı yorumu şudur:</p>



<p class="wp-block-paragraph"><strong>Bir modülün değişmesi için tek bir temel neden bulunmalıdır.</strong></p>



<p class="wp-block-paragraph">Buradaki değişiklik nedenini, modülün hizmet ettiği iş sorumluluğu veya ilgili paydaşın beklentisi üzerinden değerlendirebiliriz.</p>



<p class="wp-block-paragraph">Bir sınıfta birden fazla metot bulunması tek başına SRP ihlali değildir.</p>



<h3 class="wp-block-heading">SRP İhlali: Her Şeyi Yöneten OrderService</h3>



<p class="wp-block-paragraph">Bir sipariş servisi yazdığımızı düşünelim.</p>



<pre class="wp-block-code language-java"><code>import java.math.BigDecimal;

public class OrderService {

    public void createOrder(String customerEmail,
                            BigDecimal amount) {

        // Sipariş kaydını oluştur.
        System.out.println("Order created");

        // Ödemeyi al.
        System.out.println("Payment: " + amount);

        // E-posta gönder.
        System.out.println("Email: " + customerEmail);

        // Rapor oluştur.
        System.out.println("Report updated");
    }
}</code></pre>



<p class="wp-block-paragraph">Bu örnekte <code>OrderService</code> dört farklı sorumluluk taşıyor.</p>



<p class="wp-block-paragraph">Sipariş yönetimi ekibi sipariş kurallarını değiştirebilir. Finans ekibi ödeme akışına yeni kontroller ekleyebilir. Bildirim ekibi SMS desteği isteyebilir. Raporlama ekibi ise yeni bir analitik servisine geçebilir.</p>



<p class="wp-block-paragraph">Bütün bu değişiklikler aynı sınıfı etkiliyor.</p>



<p class="wp-block-paragraph">Kod başlangıçta küçük görünse bile gerçek servislerle entegrasyon kurduğumuzda sınıf hızla büyür.</p>



<h3 class="wp-block-heading">SRP&#8217;ye Uygun Tasarım</h3>



<p class="wp-block-paragraph">Önce sipariş verisini tanımlayalım.</p>



<pre class="wp-block-code language-java"><code>import java.math.BigDecimal;
import java.util.Objects;

public record Order(
        String id,
        String customerEmail,
        BigDecimal amount) {

    public Order {
        Objects.requireNonNull(id);
        Objects.requireNonNull(customerEmail);
        Objects.requireNonNull(amount);

        if (amount.signum() &lt; 0) {
            throw new IllegalArgumentException(
                    "Sipariş tutarı negatif olamaz"
            );
        }
    }
}</code></pre>



<p class="wp-block-paragraph">Java 17 ile kullanabileceğimiz <code>record</code>, veri modelinin tekrar eden kodunu azaltır.</p>



<p class="wp-block-paragraph">Ardından sorumlulukları ayıralım.</p>



<pre class="wp-block-code language-java"><code>interface OrderRepository {
    void save(Order order);
}

interface PaymentProcessor {
    void charge(Order order);
}

interface OrderNotifier {
    void notifyCustomer(Order order);
}</code></pre>



<p class="wp-block-paragraph">Şimdi iş akışını koordine eden servisi oluşturalım.</p>



<pre class="wp-block-code language-java"><code>public final class OrderService {

    private final OrderRepository repository;
    private final PaymentProcessor paymentProcessor;
    private final OrderNotifier notifier;

    public OrderService(
            OrderRepository repository,
            PaymentProcessor paymentProcessor,
            OrderNotifier notifier) {

        this.repository = repository;
        this.paymentProcessor = paymentProcessor;
        this.notifier = notifier;
    }

    public void createOrder(Order order) {
        repository.save(order);
        paymentProcessor.charge(order);
        notifier.notifyCustomer(order);
    }
}</code></pre>



<p class="wp-block-paragraph"><code>OrderService</code> artık sipariş oluşturma akışını koordine eder. Veritabanına nasıl yazacağımızı, ödemeyi hangi sağlayıcıyla alacağımızı veya müşteriye hangi kanaldan ulaşacağımızı bilmez.</p>



<p class="wp-block-paragraph">Burada önemli bir ayrıntı var: <code>OrderService</code> üç ayrı bileşeni çağırdığı için SRP&#8217;yi ihlal etmiş sayılmaz. Servisin temel sorumluluğu sipariş oluşturma sürecini koordine etmektir.</p>



<h3 class="wp-block-heading">Gerçek Projede Neye Dikkat Etmeliyiz?</h3>



<p class="wp-block-paragraph">Bu kod sorumluluk ayrımını gösterir, ancak üretim ortamına hazır bir sipariş akışı değildir.</p>



<p class="wp-block-paragraph">Örneğin veritabanına sipariş kaydettikten sonra ödeme başarısız olursa ne olacak? Ödeme başarılı olurken bildirim servisi hata verirse işlemi geri alacak mıyız?</p>



<p class="wp-block-paragraph">Gerçek projede sipariş durumlarını, ödeme idempotency anahtarlarını ve hata sonrası telafi işlemlerini ayrıca tasarlamalıyız.</p>



<p class="wp-block-paragraph">SRP bu kararların yerine geçmez. Yalnızca ilgili sorumlulukları doğru bileşenlerde tutmamıza yardımcı olur.</p>



<p class="wp-block-paragraph"><strong>SRP ne zaman önem kazanır?</strong> Bir sınıfın farklı iş ekiplerinden gelen değişiklikler nedeniyle sık sık güncelleme gerektirdiği durumlarda SRP&#8217;yi yeniden değerlendirin.</p>



<h2 class="wp-block-heading">2. Open/Closed Principle (OCP)</h2>



<p class="wp-block-paragraph">Open/Closed Principle, Açık/Kapalı Prensibi olarak bilinir.</p>



<p class="wp-block-paragraph">Bertrand Meyer&#8217;in ortaya koyduğu yaklaşım, yazılım bileşenlerinin genişlemeye açık, mevcut davranışı değiştirmeye ise mümkün olduğunca kapalı olmasını hedefler.</p>



<p class="wp-block-paragraph">Buradaki amaç hiçbir zaman mevcut kodu değiştirmemek değildir.</p>



<p class="wp-block-paragraph">Amaç, yeni bir davranış eklerken mevcut ve doğruluğunu testlerle desteklediğimiz kod üzerinde gereksiz değişiklik yapmamaktır.</p>



<h3 class="wp-block-heading">OCP İhlali: İndirim Hesaplama</h3>



<p class="wp-block-paragraph">E-ticaret uygulamamızda standart, premium ve kurumsal müşteriler için farklı indirim oranları bulunsun.</p>



<pre class="wp-block-code language-java"><code>import java.math.BigDecimal;

public class DiscountService {

    public BigDecimal calculate(
            BigDecimal amount,
            String customerType) {

        if ("STANDARD".equals(customerType)) {
            return BigDecimal.ZERO;
        }

        if ("PREMIUM".equals(customerType)) {
            return amount.multiply(
                    new BigDecimal("0.10")
            );
        }

        if ("CORPORATE".equals(customerType)) {
            return amount.multiply(
                    new BigDecimal("0.20")
            );
        }

        throw new IllegalArgumentException(
                "Unknown customer type"
        );
    }
}</code></pre>



<p class="wp-block-paragraph">Yeni bir müşteri tipi eklediğimizde <code>DiscountService</code> kodunu değiştirmek zorundayız.</p>



<p class="wp-block-paragraph">Üstelik her yeni koşul, mevcut davranışları yanlışlıkla etkileme ihtimalini artırır.</p>



<h3 class="wp-block-heading">OCP Çözümü: Strategy Pattern</h3>



<p class="wp-block-paragraph">İndirim algoritmasını bir arayüzle temsil edelim.</p>



<pre class="wp-block-code language-java"><code>import java.math.BigDecimal;

@FunctionalInterface
public interface DiscountPolicy {

    BigDecimal calculate(BigDecimal amount);
}</code></pre>



<p class="wp-block-paragraph">Premium indirim politikası:</p>



<pre class="wp-block-code language-java"><code>public final class PremiumDiscount
        implements DiscountPolicy {

    private static final BigDecimal RATE =
            new BigDecimal("0.10");

    @Override
    public BigDecimal calculate(BigDecimal amount) {
        return amount.multiply(RATE);
    }
}</code></pre>



<p class="wp-block-paragraph">Kurumsal müşteri politikası:</p>



<pre class="wp-block-code language-java"><code>public final class CorporateDiscount
        implements DiscountPolicy {

    private static final BigDecimal RATE =
            new BigDecimal("0.20");

    @Override
    public BigDecimal calculate(BigDecimal amount) {
        return amount.multiply(RATE);
    }
}</code></pre>



<p class="wp-block-paragraph">Şimdi indirim servisini düzenleyelim.</p>



<pre class="wp-block-code language-java"><code>import java.math.BigDecimal;
import java.util.Objects;

public final class DiscountService {

    private final DiscountPolicy policy;

    public DiscountService(DiscountPolicy policy) {
        this.policy = Objects.requireNonNull(policy);
    }

    public BigDecimal calculateDiscount(
            BigDecimal amount) {

        Objects.requireNonNull(amount);

        if (amount.signum() &lt; 0) {
            throw new IllegalArgumentException(
                    "Tutar negatif olamaz"
            );
        }

        BigDecimal discount = policy.calculate(amount);

        if (discount == null
                || discount.signum() &lt; 0
                || discount.compareTo(amount) &gt; 0) {

            throw new IllegalArgumentException(
                    "Geçersiz indirim tutarı"
            );
        }

        return discount;
    }
}</code></pre>



<p class="wp-block-paragraph">Kullanım:</p>



<pre class="wp-block-code language-java"><code>DiscountService service =
        new DiscountService(
                new PremiumDiscount()
        );

BigDecimal discount = service.calculateDiscount(
        new BigDecimal("1000.00")
);

System.out.println(discount); // 100.0000</code></pre>



<p class="wp-block-paragraph">Artık yeni bir kampanya için farklı bir <code>DiscountPolicy</code> uygulaması yazabiliriz.</p>



<p class="wp-block-paragraph"><code>DiscountService</code> ise aynı sözleşme üzerinden çalışmaya devam eder.</p>



<h3 class="wp-block-heading">Java Lambda ile Daha Kısa Bir Çözüm</h3>



<p class="wp-block-paragraph"><code>DiscountPolicy</code> fonksiyonel bir arayüz olduğu için Java lambda ifadelerini kullanabiliriz.</p>



<pre class="wp-block-code language-java"><code>DiscountPolicy seasonalDiscount =
        amount -&gt; amount.multiply(
                new BigDecimal("0.15")
        );

DiscountService service =
        new DiscountService(seasonalDiscount);</code></pre>



<p class="wp-block-paragraph">Bu yaklaşım küçük, durum tutmayan hesaplamalar için yeterli olabilir.</p>



<p class="wp-block-paragraph">Ancak kampanya yönetimi farklı veritabanı sorguları, koşullar veya harici servisler gerektiriyorsa ayrı bir sınıf daha anlamlıdır.</p>



<h3 class="wp-block-heading">OCP&#8217;nin Sınırları</h3>



<p class="wp-block-paragraph">Strategy kullanmak yeni bir algoritmanın sistem tarafından otomatik olarak keşfedileceği anlamına gelmez.</p>



<p class="wp-block-paragraph">Örneğin stratejileri seçen bir factory veya registry varsa yeni davranışı oraya tanıtmanız gerekebilir.</p>



<p class="wp-block-paragraph">Ayrıca her yeni iş kuralı için interface oluşturmak doğru değildir. Değişim ihtiyacını destekleyen gerçek bir nedeniniz olmalı.</p>



<p class="wp-block-paragraph"><strong>OCP ne zaman önem kazanır?</strong> Yeni varyasyonlar aynı kod bloğunda sürekli yeni koşullar oluşturuyorsa genişletilebilir bir davranış modeli değerlendirin.</p>



<h2 class="wp-block-heading">3. Liskov Substitution Principle (LSP)</h2>



<p class="wp-block-paragraph">Liskov Substitution Principle, Barbara Liskov&#8217;un alt türler ve davranışsal sözleşmeler üzerine geliştirdiği yaklaşıma dayanır.</p>



<p class="wp-block-paragraph">İlkenin temel düşüncesi şudur:</p>



<p class="wp-block-paragraph"><strong>Bir alt tür, üst türün beklentilerini bozmadan onun yerine kullanılabilmelidir.</strong></p>



<p class="wp-block-paragraph">Burada yalnızca Java derleyicisinin tür uyumluluğunu kontrol etmesi yeterli değildir.</p>



<p class="wp-block-paragraph">Metotların ön koşulları, son koşulları, hata davranışları ve nesnenin koruması gereken kurallar da önem taşır.</p>



<h3 class="wp-block-heading">LSP İhlali: Bütün Ödemeler İptal Edilebilir mi?</h3>



<p class="wp-block-paragraph">Bir ödeme sisteminin hem kredi kartı ödemelerini hem de belirli hediye kartı ödemelerini desteklediğini düşünelim.</p>



<p class="wp-block-paragraph">İlk tasarımda bütün ödeme yöntemleri için aynı arayüzü kullanalım.</p>



<pre class="wp-block-code language-java"><code>import java.math.BigDecimal;

interface PaymentMethod {

    void pay(BigDecimal amount);

    void refund(BigDecimal amount);
}</code></pre>



<p class="wp-block-paragraph">Kredi kartı uygulaması:</p>



<pre class="wp-block-code language-java"><code>class CreditCardPayment implements PaymentMethod {

    @Override
    public void pay(BigDecimal amount) {
        System.out.println("Card payment");
    }

    @Override
    public void refund(BigDecimal amount) {
        System.out.println("Card refund");
    }
}</code></pre>



<p class="wp-block-paragraph">Belirli bir hediye kartı ürününün iadeyi desteklemediğini varsayalım.</p>



<pre class="wp-block-code language-java"><code>class GiftCardPayment implements PaymentMethod {

    @Override
    public void pay(BigDecimal amount) {
        System.out.println("Gift card payment");
    }

    @Override
    public void refund(BigDecimal amount) {
        throw new UnsupportedOperationException(
                "Refund is not supported"
        );
    }
}</code></pre>



<p class="wp-block-paragraph"><code>GiftCardPayment</code>, Java tür sistemi açısından geçerli bir <code>PaymentMethod</code> uygulamasıdır.</p>



<p class="wp-block-paragraph">Ancak arayüzün sözleşmesi bütün ödeme yöntemlerinin iade yapabildiğini vaat ediyorsa bu sınıf beklentiyi karşılamaz.</p>



<p class="wp-block-paragraph">İstemci güvenle <code>refund()</code> çağıracağını düşünürken çalışma zamanında hata alır.</p>



<p class="wp-block-paragraph">Bu örnekte hata, somut türün üst tür sözleşmesini yerine getirememesidir.</p>



<h3 class="wp-block-heading">LSP&#8217;ye Uygun Arayüz Tasarımı</h3>



<p class="wp-block-paragraph">Bütün ödeme yöntemlerinin ödeme yapabildiğini, ancak yalnızca belirli yöntemlerin iade desteklediğini açık biçimde modelleyelim.</p>



<pre class="wp-block-code language-java"><code>import java.math.BigDecimal;

interface PaymentMethod {
    void pay(BigDecimal amount);
}

interface RefundablePayment extends PaymentMethod {
    void refund(BigDecimal amount);
}</code></pre>



<p class="wp-block-paragraph">Kredi kartı:</p>



<pre class="wp-block-code language-java"><code>final class CreditCardPayment
        implements RefundablePayment {

    @Override
    public void pay(BigDecimal amount) {
        System.out.println("Card payment");
    }

    @Override
    public void refund(BigDecimal amount) {
        System.out.println("Card refund");
    }
}</code></pre>



<p class="wp-block-paragraph">Hediye kartı:</p>



<pre class="wp-block-code language-java"><code>final class GiftCardPayment
        implements PaymentMethod {

    @Override
    public void pay(BigDecimal amount) {
        System.out.println("Gift card payment");
    }
}</code></pre>



<p class="wp-block-paragraph">İade servisi yalnızca iade yeteneğine sahip ödeme türleriyle çalışsın.</p>



<pre class="wp-block-code language-java"><code>import java.math.BigDecimal;
import java.util.Objects;

final class RefundService {

    private final RefundablePayment payment;

    RefundService(RefundablePayment payment) {
        this.payment = Objects.requireNonNull(payment);
    }

    void refund(BigDecimal amount) {
        payment.refund(amount);
    }
}</code></pre>



<p class="wp-block-paragraph">Bu tasarım, bütün ödeme yöntemlerine sahip olmadıkları bir davranışı zorla eklemez.</p>



<p class="wp-block-paragraph">Ayrıca derleyici, iade yeteneği olmayan bir <code>PaymentMethod</code> nesnesini doğrudan <code>RefundService</code> constructor&#8217;ına vermemize izin vermez.</p>



<h3 class="wp-block-heading">LSP Yalnızca Exception Meselesi Değildir</h3>



<p class="wp-block-paragraph">Bir alt tür metot imzasını koruduğu hâlde farklı semantik davranış gösterebilir.</p>



<p class="wp-block-paragraph">Örneğin:</p>



<ul class="wp-block-list">
<li>Üst tür negatif olmayan tutarlar kabul ederken alt tür yalnızca belirli bir tutarın üzerindeki işlemleri kabul edebilir.</li>



<li>Üst tür başarılı işlem sonrasında kesin kayıt oluşturacağını vaat ederken alt tür sessizce işlemi atlayabilir.</li>



<li>Üst tür belirli bir hata sözleşmesi sunarken alt tür ilgisiz bir istisna fırlatabilir.</li>
</ul>



<p class="wp-block-paragraph">Bu durumlarda Java&#8217;nın derleme zamanı kontrolleri her davranışsal uyumsuzluğu yakalayamaz.</p>



<p class="wp-block-paragraph">Bu yüzden LSP&#8217;yi test ederken yalnızca metotların varlığını değil, sözleşmenin korunduğunu da doğrulamalıyız.</p>



<p class="wp-block-paragraph"><strong>LSP ne zaman önem kazanır?</strong> Kalıtım veya arayüz polimorfizmi kullanan kodlarda somut uygulamaların istemci beklentilerini değiştirdiğini fark ediyorsanız LSP&#8217;yi inceleyin.</p>



<h2 class="wp-block-heading">4. Interface Segregation Principle (ISP)</h2>



<p class="wp-block-paragraph">Interface Segregation Principle, Arayüz Ayrımı Prensibi olarak bilinir.</p>



<p class="wp-block-paragraph">Bu ilke, istemcilerin ihtiyaç duymadıkları metotlara bağımlı hâle gelmemesini önerir.</p>



<p class="wp-block-paragraph">Bir arayüz çok sayıda sorumluluk içerdiğinde onu uygulayan sınıflar gereksiz metotlar taşımak zorunda kalabilir.</p>



<h3 class="wp-block-heading">ISP İhlali: Büyük Bir Raporlama Arayüzü</h3>



<p class="wp-block-paragraph">E-ticaret uygulamamız için rapor hazırlayan bir servis geliştirdiğimizi düşünelim.</p>



<pre class="wp-block-code language-java"><code>interface ReportService {

    void generatePdf();

    void generateExcel();

    void sendEmail();

    void uploadToCloud();
}</code></pre>



<p class="wp-block-paragraph">Bir PDF raporlama servisi yalnızca PDF üretmek isteyebilir. Ancak arayüz bütün metotları uygulamasını zorunlu kılar.</p>



<pre class="wp-block-code language-java"><code>class PdfReportService implements ReportService {

    @Override
    public void generatePdf() {
        System.out.println("PDF generated");
    }

    @Override
    public void generateExcel() {
        throw new UnsupportedOperationException();
    }

    @Override
    public void sendEmail() {
        throw new UnsupportedOperationException();
    }

    @Override
    public void uploadToCloud() {
        throw new UnsupportedOperationException();
    }
}</code></pre>



<p class="wp-block-paragraph">Bu tasarımda arayüz farklı yetenekleri gereksiz biçimde tek sözleşmede toplar.</p>



<h3 class="wp-block-heading">ISP Çözümü: Küçük ve Odaklı Arayüzler</h3>



<p class="wp-block-paragraph">Rapor üretme işlemini veri formatı üzerinden ayıralım.</p>



<pre class="wp-block-code language-java"><code>interface PdfGenerator {
    byte&#91;] generatePdf();
}

interface ExcelGenerator {
    byte&#91;] generateExcel();
}</code></pre>



<p class="wp-block-paragraph">Dağıtım yeteneklerini de ayrı tanımlayalım.</p>



<pre class="wp-block-code language-java"><code>interface EmailSender {
    void send(String recipient, byte&#91;] content);
}

interface CloudUploader {
    void upload(String key, byte&#91;] content);
}</code></pre>



<p class="wp-block-paragraph">Artık PDF üreten bir servis yalnızca ihtiyacı olan sözleşmeyi uygulayabilir.</p>



<pre class="wp-block-code language-java"><code>import java.nio.charset.StandardCharsets;

final class PdfReportGenerator implements PdfGenerator {

    @Override
    public byte&#91;] generatePdf() {
        // Örnek veri: gerçek PDF üretimi değildir.
        return "PDF example"
                .getBytes(StandardCharsets.UTF_8);
    }
}</code></pre>



<p class="wp-block-paragraph">PDF oluşturma ve e-posta gönderme işlemlerini birleştiren servis ise her iki bağımlılığı dışarıdan alabilir.</p>



<pre class="wp-block-code language-java"><code>import java.util.Objects;

final class ReportDeliveryService {

    private final PdfGenerator generator;
    private final EmailSender emailSender;

    ReportDeliveryService(
            PdfGenerator generator,
            EmailSender emailSender) {

        this.generator = Objects.requireNonNull(generator);
        this.emailSender = Objects.requireNonNull(emailSender);
    }

    void deliver(String recipient) {
        byte&#91;] report = generator.generatePdf();
        emailSender.send(recipient, report);
    }
}</code></pre>



<p class="wp-block-paragraph">Bu tasarımda PDF üreticisini Excel üretmeye zorlamıyoruz. E-posta gönderme yeteneğini de rapor oluşturma sorumluluğuyla karıştırmıyoruz.</p>



<h3 class="wp-block-heading">Her Metot İçin Ayrı Arayüz Açmalı mıyız?</h3>



<p class="wp-block-paragraph">Hayır.</p>



<p class="wp-block-paragraph">ISP&#8217;nin amacı arayüzleri olabildiğince küçültmek değildir.</p>



<p class="wp-block-paragraph">Birbiriyle anlamlı ilişki taşıyan metotlar aynı sözleşmede bulunabilir. Örneğin dosya okuma işlemlerini ele alan bir arayüzün <code>read()</code> ve <code>close()</code> davranışlarını birlikte sunması makul olabilir.</p>



<p class="wp-block-paragraph">Asıl soru, arayüzü kullanan istemcilerin hangi yeteneklere ihtiyaç duyduğudur.</p>



<p class="wp-block-paragraph"><strong>ISP ne zaman önem kazanır?</strong> Bir sınıf arayüz metotlarının büyük kısmına ihtiyaç duymuyorsa veya sık sık <code>UnsupportedOperationException</code> yazıyorsanız arayüzü yeniden tasarlayın.</p>



<h2 class="wp-block-heading">5. Dependency Inversion Principle (DIP)</h2>



<p class="wp-block-paragraph">Dependency Inversion Principle, Bağımlılıkların Tersine Çevrilmesi Prensibi olarak bilinir.</p>



<p class="wp-block-paragraph">Bu ilkeye göre üst seviye iş kuralları, alt seviye altyapı ayrıntılarına doğrudan bağımlı olmamalıdır.</p>



<p class="wp-block-paragraph">Her iki taraf da uygun soyutlamalara dayanmalıdır.</p>



<p class="wp-block-paragraph">Ayrıca soyutlamalar altyapı ayrıntılarına değil, ayrıntılar soyutlamaların sunduğu sözleşmelere uyum sağlamalıdır.</p>



<h3 class="wp-block-heading">DIP İhlali: OrderService ve Veritabanı Bağımlılığı</h3>



<p class="wp-block-paragraph">Bir sipariş servisi doğrudan Oracle veritabanına erişen sınıfı oluşturuyor olsun.</p>



<pre class="wp-block-code language-java"><code>class OracleOrderRepository {

    void save(Order order) {
        System.out.println("Saving to Oracle");
    }
}</code></pre>



<p class="wp-block-paragraph">Sipariş servisi:</p>



<pre class="wp-block-code language-java"><code>class OrderService {

    private final OracleOrderRepository repository =
            new OracleOrderRepository();

    void createOrder(Order order) {
        repository.save(order);
    }
}</code></pre>



<p class="wp-block-paragraph">Bu yaklaşımda üst seviye iş akışı doğrudan belirli bir veritabanı bileşenine bağımlıdır.</p>



<p class="wp-block-paragraph">Test ortamında farklı bir repository kullanmak istediğimizde <code>OrderService</code> koduna müdahale etmemiz gerekebilir.</p>



<p class="wp-block-paragraph">Ayrıca bu somut bağımlılık, sipariş iş kuralları ile kalıcılık teknolojisi arasında gereksiz bağ oluşturur.</p>



<h3 class="wp-block-heading">DIP Çözümü: Repository Soyutlaması</h3>



<p class="wp-block-paragraph">Sipariş yönetiminin ihtiyaç duyduğu sözleşmeyi tanımlayalım.</p>



<pre class="wp-block-code language-java"><code>interface OrderRepository {

    void save(Order order);
}</code></pre>



<p class="wp-block-paragraph">Oracle adaptörü:</p>



<pre class="wp-block-code language-java"><code>final class OracleOrderRepository
        implements OrderRepository {

    @Override
    public void save(Order order) {
        System.out.println(
                "Saving order to Oracle: " + order.id()
        );
    }
}</code></pre>



<p class="wp-block-paragraph">Sipariş servisi:</p>



<pre class="wp-block-code language-java"><code>import java.util.Objects;

final class OrderService {

    private final OrderRepository repository;

    OrderService(OrderRepository repository) {
        this.repository = Objects.requireNonNull(repository);
    }

    void createOrder(Order order) {
        repository.save(order);
    }
}</code></pre>



<p class="wp-block-paragraph">Artık bağımlılığı uygulamayı başlatan kod üzerinden verebiliriz.</p>



<pre class="wp-block-code language-java"><code>OrderRepository repository =
        new OracleOrderRepository();

OrderService service =
        new OrderService(repository);</code></pre>



<p class="wp-block-paragraph">Bu örnekte <code>OrderService</code>, Oracle&#8217;a özgü bir sınıfa bağımlı değildir.</p>



<p class="wp-block-paragraph">Veritabanı erişim teknolojisini değiştirmek istediğimizde aynı <code>OrderRepository</code> sözleşmesine uygun başka bir implementasyon geliştirebiliriz.</p>



<h3 class="wp-block-heading">DIP ile Dependency Injection Aynı Şey mi?</h3>



<p class="wp-block-paragraph">Hayır.</p>



<p class="wp-block-paragraph">DIP bir tasarım prensibidir.</p>



<p class="wp-block-paragraph">Dependency Injection ise nesnenin ihtiyaç duyduğu bağımlılıkları dışarıdan verme tekniğidir.</p>



<p class="wp-block-paragraph">Constructor Injection kullanmak, DIP&#8217;yi uygulamayı kolaylaştırır. Ancak her constructor parametresi otomatik olarak doğru soyutlama anlamına gelmez.</p>



<p class="wp-block-paragraph">Örneğin <code>OrderService</code> constructor üzerinden doğrudan <code>OracleOrderRepository</code> alıyorsa bağımlılığı dışarıdan veriyor olsak bile iş katmanını somut altyapı türüne bağlamaya devam ederiz.</p>



<h3 class="wp-block-heading">Spring Boot ile DIP</h3>



<p class="wp-block-paragraph">Spring Framework, constructor injection yaklaşımını destekler.</p>



<pre class="wp-block-code language-java"><code>@Service
public class OrderService {

    private final OrderRepository repository;

    public OrderService(OrderRepository repository) {
        this.repository = repository;
    }

    public void createOrder(Order order) {
        repository.save(order);
    }
}</code></pre>



<p class="wp-block-paragraph">Bu örnekte <code>@Service</code>, Spring Framework&#8217;e ait bir annotation&#8217;dır. Projenin ilgili Spring bağımlılıklarını eklemesi gerekir.</p>



<p class="wp-block-paragraph">Spring, uygun <code>OrderRepository</code> bean&#8217;ini uygulama bağlamından çözebilir. Birden fazla uygun bean bulunduğunda ise seçim için ek yapılandırma gerekebilir.</p>



<p class="wp-block-paragraph">DIP&#8217;nin temel değeri Spring&#8217;in otomatik nesne oluşturması değildir. İş kurallarının altyapı ayrıntılarından bağımsız çalışabilmesidir.</p>



<p class="wp-block-paragraph"><strong>DIP ne zaman önem kazanır?</strong> İş mantığınız doğrudan veritabanı sürücüsüne, HTTP istemcisine, mesaj aracısına veya üçüncü taraf SDK&#8217;ya bağımlıysa DIP&#8217;yi değerlendirin.</p>



<h2 class="wp-block-heading">SOLID ve Java 17/21 Özellikleri</h2>



<p class="wp-block-paragraph">SOLID prensipleri Java&#8217;nın son sürümleriyle ortaya çıkmadı. Buna rağmen modern dil özellikleri bu prensipleri uygularken daha az kod yazmamıza yardımcı olabilir.</p>



<h3 class="wp-block-heading">Record ile Veri Modellerini Sadeleştirmek</h3>



<p class="wp-block-paragraph">Java 17&#8217;de <code>record</code>, DTO ve değer taşıyan modeller için kullanışlıdır.</p>



<pre class="wp-block-code language-java"><code>import java.math.BigDecimal;
import java.util.Objects;

public record Product(
        String id,
        String name,
        BigDecimal price) {

    public Product {
        Objects.requireNonNull(id);
        Objects.requireNonNull(name);
        Objects.requireNonNull(price);

        if (price.signum() &lt; 0) {
            throw new IllegalArgumentException(
                    "Ürün fiyatı negatif olamaz"
            );
        }
    }
}</code></pre>



<p class="wp-block-paragraph">Bu örnekte derleyici constructor, accessor, <code>equals()</code>, <code>hashCode()</code> ve <code>toString()</code> gibi temel davranışları yönetir.</p>



<p class="wp-block-paragraph">Record kullanmak tek başına SRP garantisi vermez. Ancak veri taşıma sorumluluğu bulunan modellerde gereksiz altyapı kodunu azaltır.</p>



<h3 class="wp-block-heading">Sealed Interface ile Kapalı Tür Aileleri</h3>



<p class="wp-block-paragraph">Java 17 ile kalıcı hâle gelen <code>sealed</code> türleri, bir arayüzü hangi sınıfların uygulayabileceğini sınırlar.</p>



<pre class="wp-block-code language-java"><code>sealed interface PaymentResult
        permits PaymentSuccess, PaymentFailure {
}

record PaymentSuccess(String transactionId)
        implements PaymentResult {
}

record PaymentFailure(String reason)
        implements PaymentResult {
}</code></pre>



<p class="wp-block-paragraph">Bu model, ödeme sonucunun iki temel varyasyonunu açıkça tanımlar.</p>



<p class="wp-block-paragraph">Java 21 ile pattern matching for switch özelliğini kullanarak sonuçları işleyebiliriz.</p>



<pre class="wp-block-code language-java"><code>final class PaymentResultHandler {

    static String describe(PaymentResult result) {

        return switch (result) {
            case PaymentSuccess success -&gt;
                    "Payment OK: " + success.transactionId();

            case PaymentFailure failure -&gt;
                    "Payment failed: " + failure.reason();
        };
    }
}</code></pre>



<p class="wp-block-paragraph">Derleyici kapalı tür ailesinin bütün üyelerini ele alıp almadığımızı kontrol edebilir.</p>



<p class="wp-block-paragraph">Burada OCP açısından önemli bir tasarım tercihi bulunur.</p>



<p class="wp-block-paragraph">Kapalı bir tür ailesi tanımladığımızda yeni bir ödeme sonucu türü eklemek mevcut <code>switch</code> ifadelerinin güncellenmesini gerektirebilir.</p>



<p class="wp-block-paragraph">Dolayısıyla <code>sealed interface</code>, her durumda yeni davranışlara sınırsız genişletilebilirlik sunmaz.</p>



<p class="wp-block-paragraph">Bu özellik, özellikle tür ailesini bilinçli olarak sınırlamak istediğimiz durumlarda değer kazanır.</p>



<h3 class="wp-block-heading">Java 21 Her SOLID Problemini Çözmez</h3>



<p class="wp-block-paragraph">Modern Java, bazı teknik ayrıntıları azaltır. Ancak hangi sorumluluğun hangi bileşende bulunması gerektiğine derleyici karar vermez.</p>



<p class="wp-block-paragraph">Record, yanlış sınıf sorumluluklarını düzeltmez.</p>



<p class="wp-block-paragraph">Sealed interface, hatalı bir domain sözleşmesini kendiliğinden iyileştirmez.</p>



<p class="wp-block-paragraph">Pattern matching ise bağımlılık yönünü otomatik olarak doğru hâle getirmez.</p>



<p class="wp-block-paragraph">Dil özelliklerini mimari tasarımın yerine değil, tasarım kararlarını uygularken kullanmalıyız.</p>



<h2 class="wp-block-heading">SOLID ile Tasarım Desenleri Arasındaki İlişki</h2>



<p class="wp-block-paragraph">SOLID, tasarım prensiplerini açıklar. Tasarım desenleri ise belirli problemler için çözüm yapıları sunar.</p>



<p class="wp-block-paragraph">Bu iki kavramı aynı şey gibi değerlendirmek doğru olmaz.</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><tbody><tr><th>SOLID ilkesi</th><th>İlişkili tasarım yaklaşımları</th><th>Bağlantı</th></tr><tr><td>SRP</td><td>Facade, Decorator</td><td>Sorumlulukların ayrılmasına yardımcı olabilir</td></tr><tr><td>OCP</td><td>Strategy, Factory Method</td><td>Yeni davranışların genişletilmesini kolaylaştırır</td></tr><tr><td>LSP</td><td>Strategy, Template Method</td><td>Alt türlerin davranış sözleşmeleri önem kazanır</td></tr><tr><td>ISP</td><td>Adapter, küçük arayüzler</td><td>İstemci bağımlılıklarını daraltır</td></tr><tr><td>DIP</td><td>Strategy, Repository, Dependency Injection</td><td>Soyutlamalar üzerinden bağımlılık kurar</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">Örneğin Strategy, OCP ve DIP açısından yarar sağlayabilir. Ancak kötü tasarladığımız bir Strategy arayüzü LSP&#8217;yi ihlal edebilir.</p>



<p class="wp-block-paragraph">Bu nedenle bir tasarım desenini uygulamak tek başına SOLID&#8217;e uygun kod yazdığımız anlamına gelmez.</p>



<p class="wp-block-paragraph">Java tasarım desenlerinin kategorilerini ve kullanım alanlarını daha geniş bir bakışla incelemek için serimizin ilk yazısı olan <strong>Java Tasarım Desenleri: Gerçek Hayat Örnekleri ve Modern Java</strong> rehberine göz atabilirsiniz.</p>



<h2 class="wp-block-heading">SOLID Prensiplerini Uygularken Yapılan Hatalar</h2>



<p class="wp-block-paragraph">SOLID hakkında sık karşılaştığım bir sorun, geliştiricilerin prensipleri uygulamanın amacı yerine koymasıdır.</p>



<p class="wp-block-paragraph">Kodun esnek olması önemlidir. Fakat hiçbir zaman ihtiyaç duymayacağımız varyasyonlar için büyük bir soyutlama ağı oluşturmak geliştirme maliyetini artırır.</p>



<h3 class="wp-block-heading">Her Sınıf İçin Interface Oluşturmak</h3>



<p class="wp-block-paragraph">Bir sınıfın tek bir implementasyonu bulunuyorsa otomatik olarak interface açmak zorunda değilsiniz.</p>



<p class="wp-block-paragraph">Interface, anlamlı bir sözleşme veya bağımlılık sınırı oluşturduğunda değer kazanır.</p>



<h3 class="wp-block-heading">SRP&#8217;yi Tek Metot Kuralına Dönüştürmek</h3>



<p class="wp-block-paragraph">SRP, bir sınıfın yalnızca bir metoda sahip olmasını istemez.</p>



<p class="wp-block-paragraph">Bir sipariş hesaplama bileşeni birden fazla ilişkili metot içerebilir. Önemli olan bu metotların aynı sorumluluk altında bulunmasıdır.</p>



<h3 class="wp-block-heading">OCP&#8217;yi Hiç Kod Değiştirmemek Olarak Yorumlamak</h3>



<p class="wp-block-paragraph">Gerçek iş gereksinimleri değiştiğinde mevcut kodu güncellememiz normaldir.</p>



<p class="wp-block-paragraph">OCP, bütün gelecekteki değişiklikleri önceden tahmin etmemizi beklemez.</p>



<h3 class="wp-block-heading">Kalıtımı Her Durumda Tercih Etmek</h3>



<p class="wp-block-paragraph">Kalıtım güçlü bir mekanizmadır. Ancak bir sınıfın başka bir sınıftan davranış devralması her zaman doğru bir alt tür ilişkisi oluşturmaz.</p>



<p class="wp-block-paragraph">Bazı durumlarda composition daha esnek bir çözüm sunar.</p>



<h3 class="wp-block-heading">DIP&#8217;yi Yalnızca Spring Annotation&#8217;larına İndirgemek</h3>



<p class="wp-block-paragraph"><code>@Autowired</code> veya constructor injection kullanmak tek başına doğru mimari oluşturmaz.</p>



<p class="wp-block-paragraph">İş kurallarının hangi katmanda bulunduğunu, hangi soyutlamaya bağımlı olduğunu ve bağımlılıkların hangi yönde ilerlediğini de incelemeliyiz.</p>



<h2 class="wp-block-heading">SOLID Kodlarının Test Edilebilirliği</h2>



<p class="wp-block-paragraph">SOLID&#8217;in somut yararlarından biri, sınıfları gerçek altyapı servislerinden bağımsız test edebilmemizdir.</p>



<p class="wp-block-paragraph">Örneğin DIP bölümündeki <code>OrderService</code> için gerçek Oracle bağlantısı kurmamıza gerek yok.</p>



<p class="wp-block-paragraph">JUnit 5 ile küçük bir test yazabiliriz.</p>



<pre class="wp-block-code language-java"><code>import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;

import java.math.BigDecimal;

class OrderServiceTest {

    @Test
    void shouldSaveOrder() {

        class FakeOrderRepository
                implements OrderRepository {

            Order savedOrder;

            @Override
            public void save(Order order) {
                savedOrder = order;
            }
        }

        FakeOrderRepository repository =
                new FakeOrderRepository();

        OrderService service =
                new OrderService(repository);

        Order order = new Order(
                "ORD-1001",
                "customer@example.com",
                new BigDecimal("250.00")
        );

        service.createOrder(order);

        assertEquals(order, repository.savedOrder);
    }
}</code></pre>



<p class="wp-block-paragraph">Bu testte veritabanı bağlantısı açmıyoruz.</p>



<p class="wp-block-paragraph">Test, sipariş servisinin ilgili repository sözleşmesini doğru kullanıp kullanmadığını kontrol ediyor.</p>



<p class="wp-block-paragraph">Elbette bu test Oracle veritabanına gerçekten kayıt yazabileceğimizi kanıtlamaz. Bunu doğrulamak için ayrıca entegrasyon testi geliştirmeliyiz.</p>



<p class="wp-block-paragraph">Unit test ve integration test farklı riskleri ele alır.</p>



<p class="wp-block-paragraph">SOLID burada yalnızca test yazmayı kolaylaştırır. Test stratejisini ve kapsamını yine geliştirici belirler.</p>



<h2 class="wp-block-heading">SOLID Prensiplerini Uygulamak İçin Pratik Yaklaşım</h2>



<p class="wp-block-paragraph">Mevcut bir Java projesinde SOLID uygulamak istiyorsanız bütün sınıfları aynı anda yeniden tasarlamanızı önermem.</p>



<p class="wp-block-paragraph">Bunun yerine küçük ve gözlemlenebilir adımlarla ilerleyin.</p>



<ol start="1" class="wp-block-list">
<li>Önce değişiklik maliyeti yüksek sınıfları belirleyin.</li>



<li>Sınıfların neden değiştiğini ve hangi iş sorumluluklarını taşıdığını inceleyin.</li>



<li>Mevcut davranışı koruyan karakterizasyon testleri yazın.</li>



<li>Birden fazla sorumluluğu karıştıran kodları anlamlı bileşenlere ayırın.</li>



<li>Somut altyapı bağımlılıklarını değerlendirin.</li>



<li>Yeni davranışları eklerken değişiklik yapılan dosya sayısını ve kod karmaşıklığını gözlemleyin.</li>



<li>Gereksiz soyutlamaları kaldırmaktan çekinmeyin.</li>
</ol>



<p class="wp-block-paragraph">Özellikle eski kurumsal Java uygulamalarında büyük bir refactoring çalışmasını tek seferde yapmak ciddi regresyon riski doğurabilir.</p>



<p class="wp-block-paragraph">Her değişikliği küçük adımlarla ilerletmek, testlerle doğrulamak ve sistem davranışını izlemek daha güvenli bir yaklaşım sunar.</p>



<h2 class="wp-block-heading">Sık Sorulan Sorular</h2>



<h3 class="wp-block-heading">SOLID prensipleri sadece Java için mi geçerlidir?</h3>



<p class="wp-block-paragraph">Hayır. SOLID, nesne yönelimli yazılım tasarımına yönelik genel prensiplerden oluşur. Java dışında C#, Kotlin ve C++ gibi dillerde de kullanabilirsiniz.</p>



<h3 class="wp-block-heading">SOLID prensiplerini hangi sırayla öğrenmeliyim?</h3>



<p class="wp-block-paragraph">SRP ile başlayabilir, ardından OCP ve LSP konularına geçebilirsiniz. ISP ve DIP ise arayüzler ve bağımlılık yönetimi konusunda daha derin düşünmenizi sağlar. Ancak prensipler birbirini tamamladığı için gerçek projelerde tek tek değil, birlikte değerlendirmek gerekir.</p>



<h3 class="wp-block-heading">SOLID ile Clean Code aynı şey mi?</h3>



<p class="wp-block-paragraph">Hayır. Clean Code okunabilirlik, isimlendirme, kod organizasyonu ve bakım kolaylığı gibi geniş bir alanı kapsar. SOLID ise nesne yönelimli tasarım prensiplerine odaklanır.</p>



<h3 class="wp-block-heading">SOLID ile Design Patterns arasındaki fark nedir?</h3>



<p class="wp-block-paragraph">SOLID, iyi tasarım için yol gösteren prensipler sunar. Design Patterns ise tekrar eden yazılım tasarım problemleri için çözüm modelleri önerir.</p>



<h3 class="wp-block-heading">Java Record, SOLID prensiplerinin yerini alır mı?</h3>



<p class="wp-block-paragraph">Hayır. Record, veri odaklı sınıfları daha kısa yazmamıza yardımcı olur. Sorumluluk ayrımı ve bağımlılık yönetimi gibi mimari kararların yerini alamaz.</p>



<h3 class="wp-block-heading">Spring Boot kullanırken SOLID bilmek gerekir mi?</h3>



<p class="wp-block-paragraph">Evet. Spring Boot, Dependency Injection ve farklı soyutlama mekanizmaları sunar. Ancak bu mekanizmaları hangi amaçla ve hangi mimari sınırlar içinde kullanacağımıza geliştirici karar verir.</p>



<h3 class="wp-block-heading">SOLID her projede uygulanmalı mı?</h3>



<p class="wp-block-paragraph">SOLID prensiplerini değerlendirmek yararlıdır; ancak her sınıfa aynı soyutlama düzeyini uygulamak doğru değildir. Küçük bir uygulamanın gereksinimleriyle geniş bir kurumsal sistemin gereksinimleri farklıdır.</p>



<h2 class="wp-block-heading">Sonuç: SOLID, Daha Fazla Sınıf Yazmak Demek Değildir</h2>



<p class="wp-block-paragraph">Java&#8217;da SOLID prensiplerini uygulamanın amacı mümkün olduğunca fazla interface veya sınıf oluşturmak değildir.</p>



<p class="wp-block-paragraph">SRP, sorumlulukların neden değiştiğini anlamamıza yardımcı olur. OCP, yeni davranışları mevcut kod üzerinde gereksiz değişiklik yapmadan eklemeyi hedefler. LSP, alt türlerin davranışsal sözleşmeleri korumasını ister. ISP, arayüzleri istemcilerin gerçek ihtiyaçlarına göre şekillendirir. DIP ise iş kurallarını altyapı ayrıntılarından bağımsız tutmaya odaklanır.</p>



<p class="wp-block-paragraph">Beş prensibin ortak hedefi, değişiklik maliyetini yönetilebilir seviyede tutmaktır.</p>



<p class="wp-block-paragraph">Bir tasarım kararı verirken önce gerçek problemi anlamalıyız. Ardından bu problemi çözecek en basit ve sürdürülebilir yapıyı seçebiliriz.</p>



<p class="wp-block-paragraph">Bazı durumlarda bir arayüz ve iki sınıf doğru çözümdür. Başka bir durumda tek bir <code>record</code> ve kısa bir metot aynı işi daha açık biçimde yapabilir.</p>



<p class="wp-block-paragraph">İyi Java mimarisi, her tasarım prensibini görünür biçimde kullanmakla değil, hangi durumda hangi soyutlamanın değer kazandırdığını bilmekle başlar.</p>



<p class="wp-block-paragraph"><strong>Serinin sonraki yazısı:</strong> <em>Composition vs Inheritance: Java&#8217;da Hangisini Seçmeli?</em> Bu makalede kalıtım ile nesne bileşimini Java üzerinden karşılaştıracak, LSP ve OCP ile ilişkilerini gerçek örnekler üzerinde inceleyeceğiz.</p>



<h2 class="wp-block-heading">Kaynaklar ve İleri Okuma</h2>



<ol start="1" class="wp-block-list">
<li>Robert C. Martin — <em>Agile Software Development, Principles, Patterns, and Practices</em>, Prentice Hall, 2002.</li>



<li>Robert C. Martin — <em>Clean Architecture: A Craftsman&#8217;s Guide to Software Structure and Design</em>, Prentice Hall, 2017.</li>



<li>Barbara Liskov, Jeannette Wing — <em>A Behavioral Notion of Subtyping</em>, ACM Transactions on Programming Languages and Systems, 1994.</li>



<li>Joshua Bloch — <em>Effective Java</em>, Third Edition, Addison-Wesley, 2018.</li>



<li>Oracle — Java SE 21 Language Documentation: <a href="https://docs.oracle.com/en/java/javase/21/language/">https://docs.oracle.com/en/java/javase/21/language/</a></li>



<li>OpenJDK — JEP 395: Records: <a href="https://openjdk.org/jeps/395">https://openjdk.org/jeps/395</a></li>



<li>OpenJDK — JEP 409: Sealed Classes: <a href="https://openjdk.org/jeps/409">https://openjdk.org/jeps/409</a></li>



<li>OpenJDK — JEP 441: Pattern Matching for switch: <a href="https://openjdk.org/jeps/441">https://openjdk.org/jeps/441</a></li>



<li>Martin Fowler — Refactoring: <a href="https://refactoring.com/">https://refactoring.com/</a></li>
</ol>
<p><a href="https://alikarahisar.com/blog/java-solid-prensipleri/">Java&#8217;da SOLID Prensipleri: Gerçek Kod Örnekleriyle 5 Temel İlke</a> yazısı ilk önce <a href="https://alikarahisar.com/blog">Ali Karahisar</a> üzerinde ortaya çıktı.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://alikarahisar.com/blog/java-solid-prensipleri/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Java Tasarım Desenleri (Design Patterns): Gerçek Hayat Örnekleri ve Modern Java</title>
		<link>https://alikarahisar.com/blog/java-tasarim-desenleri/</link>
					<comments>https://alikarahisar.com/blog/java-tasarim-desenleri/#respond</comments>
		
		<dc:creator><![CDATA[Ali Karahisar]]></dc:creator>
		<pubDate>Sat, 10 Oct 2026 19:56:46 +0000</pubDate>
				<category><![CDATA[Design Patterns]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Yazılım]]></category>
		<category><![CDATA[Yazılım Mimarisi]]></category>
		<category><![CDATA[Tasarım Desenleri]]></category>
		<guid isPermaLink="false">https://alikarahisar.com/blog/?p=81</guid>

					<description><![CDATA[<p>Bir Java uygulamasında yeni özellik geliştirmek genellikle kolaydır. Asıl zorluk, uygulama büyüdükçe mevcut kodu bozmadan yeni davranışlar ekleyebilmektir. Örneğin bir e-ticaret uygulaması düşünelim. İlk sürüm yalnızca kredi kartıyla ödeme kabul ediyor. Birkaç ay sonra ekip dijital cüzdan, banka transferi ve farklı ödeme sağlayıcıları eklemek istiyor. Geliştirici her ödeme yöntemi için aynı sınıfın içine yeni if-else [&#8230;]</p>
<p><a href="https://alikarahisar.com/blog/java-tasarim-desenleri/">Java Tasarım Desenleri (Design Patterns): Gerçek Hayat Örnekleri ve Modern Java</a> yazısı ilk önce <a href="https://alikarahisar.com/blog">Ali Karahisar</a> üzerinde ortaya çıktı.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Bir Java uygulamasında yeni özellik geliştirmek genellikle kolaydır. Asıl zorluk, uygulama büyüdükçe mevcut kodu bozmadan yeni davranışlar ekleyebilmektir.</p>



<p class="wp-block-paragraph">Örneğin bir e-ticaret uygulaması düşünelim. İlk sürüm yalnızca kredi kartıyla ödeme kabul ediyor. Birkaç ay sonra ekip dijital cüzdan, banka transferi ve farklı ödeme sağlayıcıları eklemek istiyor. Geliştirici her ödeme yöntemi için aynı sınıfın içine yeni <code>if-else</code> koşulları eklerse sistem zamanla bakım açısından zorlaşır.</p>



<span id="more-81"></span>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="512" src="https://alikarahisar.com/blog/wp-content/uploads/2026/10/Java-Tasarim-Desenleri_-E-Ticaret-Rehberi-1024x512.png" alt="Java_Tasarim_Desenleri_Design_Patterns" class="wp-image-84" srcset="https://alikarahisar.com/blog/wp-content/uploads/2026/10/Java-Tasarim-Desenleri_-E-Ticaret-Rehberi-1024x512.png 1024w, https://alikarahisar.com/blog/wp-content/uploads/2026/10/Java-Tasarim-Desenleri_-E-Ticaret-Rehberi-300x150.png 300w, https://alikarahisar.com/blog/wp-content/uploads/2026/10/Java-Tasarim-Desenleri_-E-Ticaret-Rehberi-768x384.png 768w, https://alikarahisar.com/blog/wp-content/uploads/2026/10/Java-Tasarim-Desenleri_-E-Ticaret-Rehberi-1536x768.png 1536w, https://alikarahisar.com/blog/wp-content/uploads/2026/10/Java-Tasarim-Desenleri_-E-Ticaret-Rehberi.png 1774w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Created By ChatGPT</figcaption></figure>



<p class="wp-block-paragraph">Strategy tasarım deseni bu soruna bir çözüm sunar. Ancak tek çözüm o değildir. Basit bir <code>switch</code> ifadesi de bazı projelerde aynı işi daha az kodla halledebilir.</p>



<p class="wp-block-paragraph"><strong>Tasarım desenleri (Design Patterns), yazılım geliştiricilerin tekrar eden tasarım problemlerini çözmek için kullandığı, deneyimle şekillenmiş çözüm yaklaşımlarıdır.</strong> Bir desen hazır kod sunmaz. Geliştiriciye sorumlulukları nasıl ayıracağını, bağımlılıkları nasıl yöneteceğini ve değişiklikleri sistemin hangi noktalarında karşılayacağını gösterir.</p>



<p class="wp-block-paragraph">Bu rehberde Java&#8217;nın en önemli tasarım desenlerini gerçek kullanım senaryolarıyla inceleyeceğiz. Java 17 ve Java 21 özelliklerinin klasik desenleri nasıl etkilediğini, hangi desenin ne zaman avantaj kazandırdığını ve hangi koşullarda gereksiz karmaşıklık yarattığını da ele alacağız.</p>



<h2 class="wp-block-heading">Tasarım Desenleri (Design Patterns) Nedir?</h2>



<p class="wp-block-paragraph">Tasarım deseni, belirli bir yazılım tasarım problemine tekrar uygulanabilen genel bir çözüm modelidir.</p>



<p class="wp-block-paragraph">Örneğin:</p>



<ul class="wp-block-list">
<li>Bir nesneden yalnızca tek örnek üretmek istiyorsanız Singleton desenini değerlendirebilirsiniz.</li>



<li>Nesne oluşturma mantığını iş kurallarından ayırmak istiyorsanız Factory yaklaşımı yardımcı olur.</li>



<li>Çok sayıda yapılandırma parametresi içeren nesneler oluşturuyorsanız Builder kullanabilirsiniz.</li>



<li>Çalışma anında farklı algoritmalar arasında geçiş yapmak istiyorsanız Strategy uygundur.</li>



<li>Bir olay gerçekleştiğinde birden fazla bileşenin haberdar olmasını istiyorsanız Observer kullanabilirsiniz.</li>
</ul>



<p class="wp-block-paragraph">Burada önemli bir ayrım var: Tasarım deseni ile yazılım mimarisi aynı şey değildir.</p>



<p class="wp-block-paragraph">Clean Architecture bir uygulamanın katmanlarını ve bağımlılık yönlerini ele alırken Strategy, belirli bir davranış ailesini nasıl yöneteceğinize odaklanır. Bu iki yaklaşım birbirini tamamlayabilir.</p>



<h3 class="wp-block-heading">Tasarım Desenleri Neden Önemlidir?</h3>



<p class="wp-block-paragraph">İyi seçilmiş bir tasarım deseni geliştiriciye birkaç somut avantaj kazandırır.</p>



<p class="wp-block-paragraph"><strong>Değişikliklerin etkisini sınırlar.</strong> Bir ödeme sağlayıcısını değiştirmek için sipariş yönetimi kodunu yeniden yazmak zorunda kalmazsınız.</p>



<p class="wp-block-paragraph"><strong>Test yazmayı kolaylaştırır.</strong> Bağımlılıkları arayüzler üzerinden yönettiğinizde gerçek servisler yerine test nesneleri kullanabilirsiniz.</p>



<p class="wp-block-paragraph"><strong>Takım içi iletişimi güçlendirir.</strong> Bir geliştiricinin &#8220;burada Strategy kullanıyoruz&#8221; demesi, uzun bir sınıf diyagramını açıklamaktan daha hızlı olabilir.</p>



<p class="wp-block-paragraph"><strong>Tekrarlanan tasarım kararlarını azaltır.</strong> Ekip, iyi anladığı çözümleri benzer problemlere tutarlı biçimde uygular.</p>



<p class="wp-block-paragraph">Bununla birlikte her tasarım deseni yeni sınıflar, arayüzler veya ek soyutlamalar getirebilir. Küçük bir problemi çözmek için gereğinden fazla katman oluşturmak kod kalitesini artırmaz.</p>



<h2 class="wp-block-heading">Java Tasarım Desenleri Kaç Kategoriye Ayrılır?</h2>



<p class="wp-block-paragraph">Gang of Four (GoF) yaklaşımı, 23 klasik tasarım desenini üç ana kategori altında inceler.</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><tbody><tr><th>Kategori</th><th>Temel amaç</th><th>Örnekler</th></tr><tr><td>Creational (Yaratımsal)</td><td>Nesne oluşturma sürecini yönetmek</td><td>Singleton, Factory Method, Abstract Factory, Builder, Prototype</td></tr><tr><td>Structural (Yapısal)</td><td>Sınıfları ve nesneleri birlikte çalıştırmak</td><td>Adapter, Decorator, Facade, Proxy, Composite, Bridge, Flyweight</td></tr><tr><td>Behavioral (Davranışsal)</td><td>Nesneler arasındaki iletişimi ve davranışları düzenlemek</td><td>Strategy, Observer, Command, State, Iterator, Mediator, Chain of Responsibility, Template Method, Visitor, Memento, Interpreter</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">Bu ayrım, hangi deseni seçeceğinizi doğrudan söylemez. Önce problemin hangi sorumlulukla ilgili olduğunu anlamanız gerekir.</p>



<p class="wp-block-paragraph">Nesne oluşturma koduyla mı uğraşıyorsunuz? Birden fazla sistemi mi bütünleştiriyorsunuz? Yoksa çalışma anında değişen iş kurallarını mı yönetiyorsunuz?</p>



<p class="wp-block-paragraph">Bu sorular desen seçimini daraltır.</p>



<h2 class="wp-block-heading">1. Singleton Pattern: Tek Bir Nesneyle Çalışmak</h2>



<p class="wp-block-paragraph">Singleton, bir sınıftan yalnızca tek bir örnek oluşturmayı ve bu örneğe merkezi bir erişim noktası sunmayı amaçlar.</p>



<p class="wp-block-paragraph">Bir uygulamanın belirli bir süreç içinde ortak kullanacağı konfigürasyon nesnesi veya durum tutmayan bir yardımcı servis bu yaklaşım için aday olabilir.</p>



<p class="wp-block-paragraph">Ancak Singleton ile global değişken kullanımı arasındaki sınır oldukça incedir.</p>



<h3 class="wp-block-heading">Java&#8217;da Singleton Nasıl Uygulanır?</h3>



<p class="wp-block-paragraph">Java&#8217;da Singleton geliştirmek için farklı yöntemler bulunur. Bunlardan biri <code>enum</code> kullanmaktır.</p>



<pre class="wp-block-code language-java"><code>public enum ApplicationConfig {
    INSTANCE;

    private final String environment = "production";

    public String getEnvironment() {
        return environment;
    }
}</code></pre>



<p class="wp-block-paragraph">Kullanım:</p>



<pre class="wp-block-code language-java"><code>String environment =
        ApplicationConfig.INSTANCE.getEnvironment();

System.out.println(environment);</code></pre>



<p class="wp-block-paragraph">Java, her enum sabiti için tek bir örnek oluşturur. Bu yaklaşım standart Java serileştirme davranışıyla uyumludur ve ek bir <code>synchronized</code> mekanizması gerektirmeden güvenli örnek oluşturma sağlar.</p>



<p class="wp-block-paragraph">Bununla birlikte enum Singleton, nesnenin içindeki değiştirilebilir verileri kendiliğinden thread-safe hâle getirmez.</p>



<h3 class="wp-block-heading">Singleton Kullanırken Nelere Dikkat Etmelisiniz?</h3>



<p class="wp-block-paragraph">Bir Singleton başka sınıflar tarafından doğrudan çağrılıyorsa bağımlılıklar kodun içine gömülür.</p>



<p class="wp-block-paragraph">Örneğin:</p>



<pre class="wp-block-code language-java"><code>public class OrderService {

    public void createOrder() {
        String env =
                ApplicationConfig.INSTANCE.getEnvironment();
    }
}</code></pre>



<p class="wp-block-paragraph"><code>OrderService</code> artık konfigürasyon kaynağını kendisi seçer. Test ortamında bu bağımlılığı değiştirmek güçleşebilir.</p>



<p class="wp-block-paragraph">Dependency Injection kullanan projelerde geliştirici çoğu zaman Singleton yerine uygulama kapsamındaki tek bir servis örneğini tercih eder.</p>



<p class="wp-block-paragraph">Spring Framework içindeki varsayılan singleton bean kapsamı buna örnektir. Spring, ilgili uygulama bağlamında bir bean tanımı için ortak bir örnek yönetir. Bu davranış, JVM genelinde tek örnek garantisi veren klasik Singleton ile aynı değildir.</p>



<p class="wp-block-paragraph"><strong>Ne zaman kullanmalı?</strong> Nesnenin süreç genelinde tek bir örnek olarak var olması gerçekten bir tasarım gereksinimiyse ve global erişimin getirdiği bağımlılıkları kabul ediyorsanız kullanın.</p>



<h2 class="wp-block-heading">2. Factory Pattern: Nesne Oluşturma Mantığını Ayırmak</h2>



<p class="wp-block-paragraph">Bir uygulama farklı bildirim kanalları üzerinden mesaj gönderebilir: e-posta, SMS veya anlık bildirim.</p>



<p class="wp-block-paragraph">Her iş servisi hangi sınıfı oluşturacağını kendisi seçerse aynı karar mantığı uygulamanın farklı noktalarına dağılır.</p>



<p class="wp-block-paragraph">Factory yaklaşımı nesne oluşturma kararını merkezi bir bileşene taşır.</p>



<p class="wp-block-paragraph">Burada terminolojiye dikkat etmek gerekir. Gündelik Java geliştirmesinde <code>Factory Pattern</code> olarak anılan basit statik fabrika yaklaşımı, GoF kitabındaki kalıtım temelli <strong>Factory Method</strong> deseniyle birebir aynı değildir.</p>



<h3 class="wp-block-heading">Java 21 ile Factory Örneği</h3>



<p class="wp-block-paragraph">Önce bildirim modellerini tanımlayalım:</p>



<pre class="wp-block-code language-java"><code>sealed interface Notification
        permits EmailNotification, SmsNotification {
}

record EmailNotification(String address)
        implements Notification {
}

record SmsNotification(String phoneNumber)
        implements Notification {
}</code></pre>



<p class="wp-block-paragraph">Ardından her kanal için farklı bir gönderici oluşturalım:</p>



<pre class="wp-block-code language-java"><code>interface NotificationSender {
    void send(String message);
}

final class EmailSender implements NotificationSender {

    @Override
    public void send(String message) {
        System.out.println("Email: " + message);
    }
}

final class SmsSender implements NotificationSender {

    @Override
    public void send(String message) {
        System.out.println("SMS: " + message);
    }
}</code></pre>



<p class="wp-block-paragraph">Factory sınıfı:</p>



<pre class="wp-block-code language-java"><code>final class NotificationFactory {

    private NotificationFactory() {
    }

    static NotificationSender create(
            Notification notification) {

        return switch (notification) {
            case EmailNotification email -&gt;
                    new EmailSender();

            case SmsNotification sms -&gt;
                    new SmsSender();
        };
    }
}</code></pre>



<p class="wp-block-paragraph">Kullanım:</p>



<pre class="wp-block-code language-java"><code>Notification notification =
        new EmailNotification("user@example.com");

NotificationSender sender =
        NotificationFactory.create(notification);

sender.send("Siparişiniz onaylandı.");</code></pre>



<h3 class="wp-block-heading">Bu Tasarım Neden İşe Yarar?</h3>



<p class="wp-block-paragraph"><code>NotificationFactory</code> nesnenin somut türünü seçer. İstemci ise oluşturduğu nesneyle <code>NotificationSender</code> arayüzü üzerinden iletişim kurar.</p>



<p class="wp-block-paragraph">Java 17 ile kalıcı hâle gelen <code>sealed</code> yapısı, hangi türlerin <code>Notification</code> arayüzünü uygulayabileceğini sınırlar.</p>



<p class="wp-block-paragraph">Java 21 ile kalıcı hâle gelen <code>switch</code> pattern matching özelliği ise bu türler üzerinde kapsamlı bir eşleştirme yapar.</p>



<p class="wp-block-paragraph">Yeni bir bildirim türünü <code>permits</code> kapsamına eklediğinizde derleyici, ilgili <code>switch</code> ifadesinin yeni türü de ele almasını isteyebilir.</p>



<p class="wp-block-paragraph">Bu yaklaşımın bir sınırı var: Factory, yeni somut türler karşısında değişiklik gerektirir. Kapalı bir tür ailesi için bu davranış avantaj sağlar. Dinamik olarak genişleyen bir eklenti mimarisinde ise registry tabanlı bir fabrika veya Dependency Injection daha uygun olabilir.</p>



<p class="wp-block-paragraph"><strong>Ne zaman kullanmalı?</strong> Nesne oluşturma kararı birden fazla yerde tekrar ediyorsa veya nesne oluştururken iş kurallarına bağlı seçim yapıyorsanız Factory yaklaşımını değerlendirin.</p>



<h2 class="wp-block-heading">3. Builder Pattern: Karmaşık Nesneleri Adım Adım Oluşturmak</h2>



<p class="wp-block-paragraph">Bir API isteği düşünelim. Kullanıcı, rapor oluştururken tarih aralığı, format, dil, filtreler ve sıralama tercihi belirleyebiliyor.</p>



<p class="wp-block-paragraph">Bütün parametreleri tek constructor üzerinden almak zamanla okunabilirliği azaltabilir.</p>



<p class="wp-block-paragraph">Builder deseni, karmaşık bir nesnenin oluşturulma sürecini anlaşılır adımlara böler.</p>



<h3 class="wp-block-heading">Java Builder Örneği</h3>



<pre class="wp-block-code language-java"><code>import java.time.LocalDate;
import java.util.Objects;

public final class ReportRequest {

    private final LocalDate startDate;
    private final LocalDate endDate;
    private final String format;

    private ReportRequest(Builder builder) {
        this.startDate = builder.startDate;
        this.endDate = builder.endDate;
        this.format = builder.format;
    }

    public static Builder builder() {
        return new Builder();
    }

    public static final class Builder {

        private LocalDate startDate;
        private LocalDate endDate;
        private String format = "PDF";

        public Builder startDate(LocalDate value) {
            this.startDate = value;
            return this;
        }

        public Builder endDate(LocalDate value) {
            this.endDate = value;
            return this;
        }

        public Builder format(String value) {
            this.format = value;
            return this;
        }

        public ReportRequest build() {
            Objects.requireNonNull(startDate);
            Objects.requireNonNull(endDate);
            Objects.requireNonNull(format);

            if (startDate.isAfter(endDate)) {
                throw new IllegalArgumentException(
                        "Başlangıç tarihi bitişten sonra olamaz."
                );
            }

            return new ReportRequest(this);
        }
    }
}</code></pre>



<p class="wp-block-paragraph">Kullanım:</p>



<pre class="wp-block-code language-java"><code>ReportRequest request = ReportRequest.builder()
        .startDate(LocalDate.of(2026, 1, 1))
        .endDate(LocalDate.of(2026, 1, 31))
        .format("CSV")
        .build();</code></pre>



<p class="wp-block-paragraph">Builder, nesne oluşmadan önce alanları toplar ve <code>build()</code> aşamasında doğrulama yapar.</p>



<p class="wp-block-paragraph">Bu sayede geçersiz tarih aralığına sahip bir <code>ReportRequest</code> nesnesi üretmeyiz.</p>



<h3 class="wp-block-heading">Java Record, Builder Deseninin Yerini Alır mı?</h3>



<p class="wp-block-paragraph">Java 17 ile <code>record</code> kullanarak veri taşıyan sınıfları çok daha kısa yazabiliriz.</p>



<pre class="wp-block-code language-java"><code>import java.time.LocalDate;
import java.util.Objects;

public record ReportFilter(
        LocalDate startDate,
        LocalDate endDate,
        String format) {

    public ReportFilter {
        Objects.requireNonNull(startDate);
        Objects.requireNonNull(endDate);
        Objects.requireNonNull(format);

        if (startDate.isAfter(endDate)) {
            throw new IllegalArgumentException(
                    "Geçersiz tarih aralığı"
            );
        }
    }
}</code></pre>



<p class="wp-block-paragraph">Üç alanlı basit bir veri modeli için bu çözüm yeterli olabilir.</p>



<p class="wp-block-paragraph">Ancak çok sayıda isteğe bağlı parametre, birbirine bağımlı yapılandırma seçeneği veya aşamalı oluşturma süreci varsa Builder hâlâ değer taşır.</p>



<p class="wp-block-paragraph">Record bileşenlerinin sonradan yeniden atanamaması da tek başına derin immutability garantisi vermez. Örneğin bir record içinde değiştirilebilir liste tutarsanız dış kod bu listenin içeriğini değiştirebilir.</p>



<p class="wp-block-paragraph"><strong>Ne zaman kullanmalı?</strong> Constructor parametreleri anlamını kaybediyorsa, çok sayıda isteğe bağlı alan varsa veya nesne oluştururken tutarlı bir doğrulama akışı gerekiyorsa Builder tercih edin.</p>



<h2 class="wp-block-heading">4. Adapter Pattern: Uyumsuz Arayüzleri Birleştirmek</h2>



<p class="wp-block-paragraph">Yazılım projelerinde her servisin aynı arayüzü kullanmasını bekleyemeyiz.</p>



<p class="wp-block-paragraph">Örneğin bir ödeme sistemi farklı bankaların servisleriyle iletişim kurabilir. Bankalardan biri <code>makePayment()</code> metodunu, diğeri <code>executeTransaction()</code> metodunu sunabilir.</p>



<p class="wp-block-paragraph">Bu farklılıkları iş katmanına taşımak, ödeme kodunu dış servislerin ayrıntılarına bağımlı hâle getirir.</p>



<p class="wp-block-paragraph">Adapter deseni, farklı arayüzleri ortak bir sözleşmeye dönüştürür.</p>



<h3 class="wp-block-heading">Java Adapter Örneği</h3>



<p class="wp-block-paragraph">Uygulamamızın ortak sözleşmesi:</p>



<pre class="wp-block-code language-java"><code>import java.math.BigDecimal;

interface PaymentGateway {

    boolean pay(BigDecimal amount);
}</code></pre>



<p class="wp-block-paragraph">Harici bankanın sunduğu servis:</p>



<pre class="wp-block-code language-java"><code>class LegacyBankClient {

    public String executeTransaction(
            BigDecimal amount) {

        return "SUCCESS";
    }
}</code></pre>



<p class="wp-block-paragraph">Adapter:</p>



<pre class="wp-block-code language-java"><code>final class LegacyBankAdapter
        implements PaymentGateway {

    private final LegacyBankClient client;

    LegacyBankAdapter(LegacyBankClient client) {
        this.client = client;
    }

    @Override
    public boolean pay(BigDecimal amount) {
        String result =
                client.executeTransaction(amount);

        return "SUCCESS".equals(result);
    }
}</code></pre>



<p class="wp-block-paragraph">Kullanım:</p>



<pre class="wp-block-code language-java"><code>PaymentGateway gateway =
        new LegacyBankAdapter(new LegacyBankClient());

boolean success =
        gateway.pay(new BigDecimal("149.90"));</code></pre>



<p class="wp-block-paragraph">Artık sipariş servisi hangi bankanın hangi metodunu kullandığını bilmek zorunda kalmaz.</p>



<h3 class="wp-block-heading">Adapter Deseninin Mimari Avantajı</h3>



<p class="wp-block-paragraph">Adapter yalnızca metot isimlerini değiştirmez. Dış sistemlerin veri modellerini ve hata sözleşmelerini de uygulamanın kendi modeline dönüştürebilir.</p>



<p class="wp-block-paragraph">Örneğin gerçek bir banka entegrasyonunda HTTP durum kodlarını, hata mesajlarını ve işlem referanslarını ortak bir sonuç nesnesine dönüştürmek isteyebilirsiniz.</p>



<p class="wp-block-paragraph">Ancak ödeme gibi kritik işlemlerde <code>"SUCCESS"</code> dışındaki her sonucu doğrudan başarısızlık olarak yorumlamak yeterli değildir. Zaman aşımı, belirsiz işlem sonucu ve kesin ret birbirinden farklı durumlardır.</p>



<p class="wp-block-paragraph">Bu ayrım olmadan aynı ödemeyi tekrar denemek çift tahsilat riski doğurabilir.</p>



<p class="wp-block-paragraph"><strong>Ne zaman kullanmalı?</strong> Üçüncü taraf SDK, eski sistem veya farklı veri sözleşmesi kullanan bir servisle entegrasyon geliştirirken Adapter deseni güçlü bir seçenektir.</p>



<h2 class="wp-block-heading">5. Decorator Pattern: Mevcut Davranışa Yeni Sorumluluklar Eklemek</h2>



<p class="wp-block-paragraph">Bir dış API istemciniz olduğunu düşünün.</p>



<p class="wp-block-paragraph">İlk sürüm yalnızca HTTP isteği gönderiyor. Daha sonra her isteğin süresini ölçmek, log üretmek veya metrik toplamak istiyorsunuz.</p>



<p class="wp-block-paragraph">Bütün bu sorumlulukları doğrudan HTTP istemcisinin içine eklemek, sınıfın kapsamını genişletir.</p>



<p class="wp-block-paragraph">Decorator deseni, mevcut nesnenin etrafına yeni bir nesne yerleştirerek davranış eklemenize olanak tanır.</p>



<h3 class="wp-block-heading">Java Decorator Örneği</h3>



<p class="wp-block-paragraph">Ortak arayüz:</p>



<pre class="wp-block-code language-java"><code>interface ApiClient {

    String fetchData();
}</code></pre>



<p class="wp-block-paragraph">Temel istemci:</p>



<pre class="wp-block-code language-java"><code>final class DefaultApiClient implements ApiClient {

    @Override
    public String fetchData() {
        return "API response";
    }
}</code></pre>



<p class="wp-block-paragraph">Zaman ölçen Decorator:</p>



<pre class="wp-block-code language-java"><code>final class TimedApiClient implements ApiClient {

    private final ApiClient delegate;

    TimedApiClient(ApiClient delegate) {
        this.delegate = delegate;
    }

    @Override
    public String fetchData() {
        long start = System.nanoTime();

        try {
            return delegate.fetchData();
        } finally {
            long elapsed = System.nanoTime() - start;

            System.out.println(
                    "Duration(ns): " + elapsed
            );
        }
    }
}</code></pre>



<p class="wp-block-paragraph">Kullanım:</p>



<pre class="wp-block-code language-java"><code>ApiClient client =
        new TimedApiClient(new DefaultApiClient());

System.out.println(client.fetchData());</code></pre>



<p class="wp-block-paragraph"><code>TimedApiClient</code>, gerçek istemcinin sorumluluğunu değiştirmez. Yalnızca işlem süresini ölçer.</p>



<p class="wp-block-paragraph">Decorator zinciri kurarak farklı davranışları birleştirebilirsiniz. Örneğin ayrı bir Decorator metrik kaydı tutarken bir diğeri erişim kontrolü gerçekleştirebilir.</p>



<h3 class="wp-block-heading">Decorator ile Inheritance Arasındaki Fark</h3>



<p class="wp-block-paragraph">Kalıtım, davranışı sınıf hiyerarşisi üzerinden genişletir. Decorator ise composition kullanır.</p>



<p class="wp-block-paragraph">Bu fark, davranış kombinasyonları arttığında önem kazanır.</p>



<p class="wp-block-paragraph">Her kombinasyon için alt sınıf üretmek yerine ihtiyacınız olan davranışları nesnelerin etrafına yerleştirebilirsiniz.</p>



<p class="wp-block-paragraph">Yine de her çapraz kesen sorumluluk için elle Decorator yazmak zorunda değilsiniz. Spring AOP, interceptor veya filtre mekanizmaları bazı durumlarda daha uygun seçeneklerdir.</p>



<p class="wp-block-paragraph"><strong>Ne zaman kullanmalı?</strong> Mevcut bir nesneye loglama, metrik, doğrulama veya önbellekleme gibi ek davranışlar kazandırmak istediğinizde değerlendirin.</p>



<h2 class="wp-block-heading">6. Strategy Pattern: Değişebilen Algoritmaları Yönetmek</h2>



<p class="wp-block-paragraph">Strategy, gerçek projelerde en sık karşılaşılan davranışsal tasarım desenlerinden biridir.</p>



<p class="wp-block-paragraph">Bir e-ticaret sisteminde farklı indirim hesaplama kuralları bulunduğunu düşünelim.</p>



<p class="wp-block-paragraph">Standart müşteriler indirim almıyor. Premium müşteriler %10 indirim alıyor. Kurumsal müşteriler ise farklı bir fiyatlandırma kuralına tabi.</p>



<p class="wp-block-paragraph">Bütün bu kuralları tek bir <code>calculateDiscount()</code> metodunda toplamak başlangıçta kolaydır. Ancak yeni kampanyalar geldikçe koşullar uzar.</p>



<p class="wp-block-paragraph">Strategy, her algoritmayı ayrı bir davranış olarak ele alır.</p>



<h3 class="wp-block-heading">Java Strategy Örneği</h3>



<p class="wp-block-paragraph">Önce ortak sözleşmeyi tanımlayalım:</p>



<pre class="wp-block-code language-java"><code>import java.math.BigDecimal;

interface DiscountStrategy {

    BigDecimal calculate(BigDecimal amount);
}</code></pre>



<p class="wp-block-paragraph">Standart müşteri stratejisi:</p>



<pre class="wp-block-code language-java"><code>final class StandardDiscount
        implements DiscountStrategy {

    @Override
    public BigDecimal calculate(BigDecimal amount) {
        return BigDecimal.ZERO;
    }
}</code></pre>



<p class="wp-block-paragraph">Premium müşteri stratejisi:</p>



<pre class="wp-block-code language-java"><code>final class PremiumDiscount
        implements DiscountStrategy {

    private static final BigDecimal RATE =
            new BigDecimal("0.10");

    @Override
    public BigDecimal calculate(BigDecimal amount) {
        return amount.multiply(RATE);
    }
}</code></pre>



<p class="wp-block-paragraph">Stratejiyi kullanan servis:</p>



<pre class="wp-block-code language-java"><code>final class PricingService {

    private final DiscountStrategy strategy;

    PricingService(DiscountStrategy strategy) {
        this.strategy = strategy;
    }

    BigDecimal calculateTotal(BigDecimal amount) {
        if (amount.signum() &lt; 0) {
            throw new IllegalArgumentException(
                    "Tutar negatif olamaz"
            );
        }

        BigDecimal discount =
                strategy.calculate(amount);

        return amount.subtract(discount);
    }
}</code></pre>



<p class="wp-block-paragraph">Kullanım:</p>



<pre class="wp-block-code language-java"><code>PricingService service =
        new PricingService(new PremiumDiscount());

BigDecimal total = service.calculateTotal(
        new BigDecimal("1000.00")
);

System.out.println(total); // 900.0000</code></pre>



<p class="wp-block-paragraph">Burada <code>PricingService</code>, indirimin nasıl hesaplandığını bilmez.</p>



<p class="wp-block-paragraph">Yeni bir indirim politikası geldiğinde geliştirici mevcut servis mantığını değiştirmeden farklı bir <code>DiscountStrategy</code> uygulaması ekleyebilir.</p>



<p class="wp-block-paragraph">Gerçek finansal uygulamalarda para birimini, yuvarlama kurallarını ve hesaplama hassasiyetini ayrıca tanımlamak gerekir. Örnekteki <code>BigDecimal</code> kullanımı yalnızca algoritma ayrımını göstermeyi amaçlar.</p>



<h3 class="wp-block-heading">Strategy ile Open/Closed Principle İlişkisi</h3>



<p class="wp-block-paragraph">Open/Closed Principle, yazılım bileşenlerinin yeni davranışlarla genişleyebilmesini ve mevcut kodda gereksiz değişiklik ihtiyacının azalmasını hedefler.</p>



<p class="wp-block-paragraph">Strategy bunu doğrudan destekler.</p>



<p class="wp-block-paragraph">Fakat bütün koşullar altında mevcut kodu değiştirmeme garantisi vermez. Strateji seçimini yöneten bileşenin yeni türleri tanıması gerekebilir.</p>



<h3 class="wp-block-heading">Her Strategy İçin Yeni Sınıf Gerekli mi?</h3>



<p class="wp-block-paragraph">Hayır.</p>



<p class="wp-block-paragraph">Java&#8217;da tek soyut metot içeren arayüzleri lambda ifadeleriyle uygulayabilirsiniz.</p>



<pre class="wp-block-code language-java"><code>DiscountStrategy campaignDiscount =
        amount -&gt; amount.multiply(
                new BigDecimal("0.15")
        );</code></pre>



<p class="wp-block-paragraph">Basit ve durum tutmayan bir algoritma için lambda yeterli olabilir.</p>



<p class="wp-block-paragraph">Buna karşılık bir strateji kendi konfigürasyonunu, bağımlılıklarını ve kapsamlı testlerini taşıyorsa ayrı sınıf kullanmak daha okunabilir bir çözüm sunar.</p>



<p class="wp-block-paragraph"><strong>Ne zaman kullanmalı?</strong> Aynı işlemin farklı algoritmalarla gerçekleşmesi gerekiyorsa ve bu algoritmaları çalışma anında değiştirmek istiyorsanız Strategy kullanın.</p>



<h2 class="wp-block-heading">7. Observer Pattern: Olay Tabanlı İletişim Kurmak</h2>



<p class="wp-block-paragraph">Bir sipariş tamamlandığında sistem birden fazla işlem başlatabilir.</p>



<p class="wp-block-paragraph">Örneğin:</p>



<ul class="wp-block-list">
<li>Müşteriye bildirim gönderir.</li>



<li>Analitik sistemine olay iletir.</li>



<li>Sadakat puanı hesaplar.</li>



<li>Denetim kaydı üretir.</li>
</ul>



<p class="wp-block-paragraph">Sipariş servisi bütün bu bileşenleri doğrudan çağırırsa zamanla çok sayıda bağımlılık taşımaya başlar.</p>



<p class="wp-block-paragraph">Observer deseni, bir olay gerçekleştiğinde ilgili dinleyicilerin haberdar olmasını sağlar.</p>



<h3 class="wp-block-heading">Java Observer Örneği</h3>



<p class="wp-block-paragraph">Önce olayı tanımlayalım:</p>



<pre class="wp-block-code language-java"><code>record OrderCreatedEvent(String orderId) {
}</code></pre>



<p class="wp-block-paragraph">Dinleyici sözleşmesi:</p>



<pre class="wp-block-code language-java"><code>interface OrderEventListener {

    void onOrderCreated(OrderCreatedEvent event);
}</code></pre>



<p class="wp-block-paragraph">Bildirim dinleyicisi:</p>



<pre class="wp-block-code language-java"><code>final class EmailNotificationListener
        implements OrderEventListener {

    @Override
    public void onOrderCreated(
            OrderCreatedEvent event) {

        System.out.println(
                "Email notification: " + event.orderId()
        );
    }
}</code></pre>



<p class="wp-block-paragraph">Olayı yöneten sınıf:</p>



<pre class="wp-block-code language-java"><code>import java.util.ArrayList;
import java.util.List;

final class OrderEventPublisher {

    private final List&lt;OrderEventListener&gt; listeners =
            new ArrayList&lt;&gt;();

    public void subscribe(OrderEventListener listener) {
        listeners.add(listener);
    }

    public void publish(OrderCreatedEvent event) {
        for (OrderEventListener listener : listeners) {
            listener.onOrderCreated(event);
        }
    }
}</code></pre>



<p class="wp-block-paragraph">Kullanım:</p>



<pre class="wp-block-code language-java"><code>OrderEventPublisher publisher =
        new OrderEventPublisher();

publisher.subscribe(
        new EmailNotificationListener()
);

publisher.publish(
        new OrderCreatedEvent("ORD-1001")
);</code></pre>



<p class="wp-block-paragraph">Publisher yalnızca dinleyici arayüzünü bilir. Bildirim gönderme ayrıntılarını bilmez.</p>



<h3 class="wp-block-heading">Observer ile Event-Driven Architecture Aynı Şey mi?</h3>



<p class="wp-block-paragraph">Hayır.</p>



<p class="wp-block-paragraph">Observer, aynı uygulama içinde çalışan nesneler arasında olay bildirimi için kullanılabilir.</p>



<p class="wp-block-paragraph">Event-Driven Architecture ise farklı servisler, mesaj aracıları, kalıcı olay akışları ve dağıtık iletişim gibi daha geniş konuları ele alır.</p>



<p class="wp-block-paragraph">Örneğin Apache Kafka üzerinden bir olay yayınlamak, klasik uygulama içi Observer kullanımından farklı operasyonel gereksinimler getirir.</p>



<p class="wp-block-paragraph">Bu örnekte <code>publish()</code> metodu senkron çalışır. Dinleyicilerden biri hata fırlatırsa sonraki dinleyiciler olayı alamayabilir.</p>



<p class="wp-block-paragraph">Gerçek bir uygulamada hata izolasyonu, tekrar deneme, olay sıralaması ve teslimat garantileri için ayrıca tasarım kararı vermelisiniz.</p>



<p class="wp-block-paragraph"><code>ArrayList</code> tabanlı abonelik yönetimi de eşzamanlı ekleme ve bildirim işlemleri için uygun değildir. Birden fazla thread dinleyici listesini değiştiriyorsa thread-safe bir yapı veya açık bir eşzamanlılık politikası seçmelisiniz.</p>



<p class="wp-block-paragraph"><strong>Ne zaman kullanmalı?</strong> Bir olayın birden fazla bağımsız bileşeni ilgilendirdiği ve yayıncı ile dinleyiciler arasında doğrudan bağımlılık kurmak istemediğiniz durumlarda tercih edin.</p>



<h2 class="wp-block-heading">8. State Pattern: Nesnenin Durumuna Göre Davranışını Değiştirmek</h2>



<p class="wp-block-paragraph">Bir siparişin farklı yaşam döngüsü durumları vardır:</p>



<p class="wp-block-paragraph"><code>CREATED</code>, <code>PAID</code>, <code>SHIPPED</code>, <code>DELIVERED</code>, <code>CANCELLED</code></p>



<p class="wp-block-paragraph">Her durum aynı işlemlere izin vermez.</p>



<p class="wp-block-paragraph">Örneğin kargoya çıkan bir siparişi doğrudan iptal etmek istemeyebilirsiniz. Teslim edilen sipariş ise farklı bir iade sürecine geçebilir.</p>



<p class="wp-block-paragraph">Karmaşık yaşam döngülerinde State deseni her durumun davranışını ayrı bir nesneyle temsil eder.</p>



<p class="wp-block-paragraph">Ancak Java 21, basit ve sınırlı durum makineleri için daha kısa bir seçenek de sunar.</p>



<h3 class="wp-block-heading">Java 21 ile Sealed State Modeli</h3>



<pre class="wp-block-code language-java"><code>sealed interface OrderState
        permits Created, Paid, Shipped, Cancelled {
}

record Created() implements OrderState {
}

record Paid() implements OrderState {
}

record Shipped() implements OrderState {
}

record Cancelled() implements OrderState {
}</code></pre>



<p class="wp-block-paragraph">Durum geçişini modelleyelim:</p>



<pre class="wp-block-code language-java"><code>final class OrderStateMachine {

    static OrderState cancel(OrderState state) {

        return switch (state) {

            case Created c -&gt; new Cancelled();

            case Paid p -&gt; new Cancelled();

            case Shipped s -&gt;
                    throw new IllegalStateException(
                            "Kargodaki sipariş iptal edilemez"
                    );

            case Cancelled c -&gt;
                    throw new IllegalStateException(
                            "Sipariş zaten iptal"
                    );
        };
    }
}</code></pre>



<p class="wp-block-paragraph">Bu kod Java 21&#8217;in sealed interface ve pattern matching özelliklerini kullanır.</p>



<p class="wp-block-paragraph">Derleyici, tanımladığımız tür ailesi üzerinden <code>switch</code> kapsamını denetleyebilir.</p>



<p class="wp-block-paragraph">Önemli bir mimari ayrım var: Bu örnek klasik nesne yönelimli State deseninin tam uygulaması değildir. Burada davranışları durum nesnelerine dağıtmak yerine merkezi bir durum geçiş fonksiyonunda topluyoruz.</p>



<p class="wp-block-paragraph">Gerçek GoF State deseni, her durumun kendi geçiş ve davranış mantığını yönetmesine olanak tanır.</p>



<h3 class="wp-block-heading">Hangi Yaklaşımı Seçmelisiniz?</h3>



<p class="wp-block-paragraph">Az sayıda durum ve birkaç geçiş içeren kapalı bir modelde <code>sealed interface</code> ile <code>switch</code> çoğu zaman yeterlidir.</p>



<p class="wp-block-paragraph">Ancak her durum farklı iş servislerini çağırıyor, kendi kurallarını yönetiyor ve yeni davranışlar ekliyorsa klasik State yaklaşımı daha uygun olabilir.</p>



<p class="wp-block-paragraph"><strong>Modern Java kullanmak, bütün tasarım desenlerini ortadan kaldırmaz. Bazı problemleri daha az soyutlamayla çözmemize yardımcı olur.</strong></p>



<h2 class="wp-block-heading">Java 17 ve Java 21 Tasarım Desenlerini Nasıl Etkiliyor?</h2>



<p class="wp-block-paragraph">Klasik tasarım desenleri, Java&#8217;nın bugünkü dil özelliklerinin büyük bir kısmından daha önce ortaya çıktı.</p>



<p class="wp-block-paragraph">Bu nedenle eski kitaplarda gördüğünüz her sınıf hiyerarşisini güncel bir Java projesinde aynı şekilde uygulamak zorunda değilsiniz.</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><tbody><tr><th>Modern Java özelliği</th><th>Tasarıma etkisi</th></tr><tr><td>Record</td><td>Basit veri modellerindeki gereksiz kod miktarını azaltır</td></tr><tr><td>Sealed Class / Interface</td><td>Kapalı tür ailelerini açıkça tanımlar</td></tr><tr><td>Pattern Matching for Switch</td><td>Türe bağlı dallanma mantığını sadeleştirir</td></tr><tr><td>Lambda Expressions</td><td>Basit Strategy ve Command uygulamalarını kısaltır</td></tr><tr><td>Method References</td><td>Fonksiyonel davranışları daha kolay aktarır</td></tr><tr><td>Virtual Threads (Java 21)</td><td>Yoğun eşzamanlı I/O işlerinde thread yönetimini kolaylaştırır</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">Virtual Threads bir tasarım deseni değildir. Java 21&#8217;in sunduğu bir eşzamanlılık özelliğidir. Ancak uygulamanın concurrency modelini tasarlarken verdiğiniz kararları etkileyebilir.</p>



<p class="wp-block-paragraph">Aynı şekilde <code>record</code> da Builder deseninin birebir karşılığı değildir. Bir dil özelliğinin hangi problemi çözdüğünü anlamadan bir desenin yerine koymak doğru sonuç vermez.</p>



<h2 class="wp-block-heading">Tasarım Desenleri ile SOLID Prensipleri Arasındaki İlişki</h2>



<p class="wp-block-paragraph">SOLID ve tasarım desenleri farklı konuları ele alır.</p>



<p class="wp-block-paragraph">SOLID prensipleri sınıf ve modül tasarımına yönelik rehberlik sunar. Tasarım desenleri ise belirli problemlere çözüm yapıları önerir.</p>



<p class="wp-block-paragraph">İkisi arasındaki ilişkiyi birkaç örnekle inceleyelim.</p>



<h3 class="wp-block-heading">Single Responsibility Principle (SRP)</h3>



<p class="wp-block-paragraph">Bir sınıfın değişmesi için birden fazla bağımsız nedeni olmaması gerekir.</p>



<p class="wp-block-paragraph">Decorator, bildirim gönderme ile performans ölçme sorumluluklarını farklı sınıflara ayırabilir.</p>



<h3 class="wp-block-heading">Open/Closed Principle (OCP)</h3>



<p class="wp-block-paragraph">Strategy, mevcut algoritma kullanıcılarını değiştirmeden yeni algoritma uygulamaları eklemeyi kolaylaştırır.</p>



<p class="wp-block-paragraph">Ancak desenin yanlış uygulanması hâlinde merkezi bir <code>if-else</code> zinciri aynı bağımlılık sorununu koruyabilir.</p>



<h3 class="wp-block-heading">Liskov Substitution Principle (LSP)</h3>



<p class="wp-block-paragraph">Bir Strategy uygulaması, arayüzün belirlediği sözleşmeye uymalıdır.</p>



<p class="wp-block-paragraph">Örneğin <code>DiscountStrategy</code> için negatif indirim değerleri yasaksa yeni bir stratejinin bu kuralı ihlal etmesi yerine geçebilirliği bozar.</p>



<h3 class="wp-block-heading">Interface Segregation Principle (ISP)</h3>



<p class="wp-block-paragraph">Küçük, amaca odaklı arayüzler Adapter ve Strategy gibi desenlerle daha iyi çalışır.</p>



<p class="wp-block-paragraph">Her davranışı kapsayan devasa bir servis arayüzü, uygulayan sınıflara gereksiz sorumluluk yükler.</p>



<h3 class="wp-block-heading">Dependency Inversion Principle (DIP)</h3>



<p class="wp-block-paragraph">Üst seviye iş kurallarını somut teknoloji sınıflarına doğrudan bağımlı hâle getirmemelisiniz.</p>



<p class="wp-block-paragraph">Örneğin <code>PricingService</code>, <code>PremiumDiscount</code> sınıfını doğrudan oluşturmak yerine <code>DiscountStrategy</code> arayüzünü kullanır.</p>



<p class="wp-block-paragraph">Bu tercih, test edilebilirliği ve bağımlılıkların yönetimini iyileştirir.</p>



<h2 class="wp-block-heading">Hangi Tasarım Deseni Ne Zaman Kullanılmalı?</h2>



<p class="wp-block-paragraph">Tasarım deseni seçerken sınıf isimlerinden önce değişiklik ihtiyacına odaklanın.</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><tbody><tr><th>Karşılaştığınız problem</th><th>Değerlendirilecek desen</th></tr><tr><td>Tek bir ortak nesne örneği gerekiyor</td><td>Singleton</td></tr><tr><td>Nesne oluşturma kuralları karmaşıklaşıyor</td><td>Factory / Factory Method</td></tr><tr><td>Nesne çok sayıda parametre içeriyor</td><td>Builder</td></tr><tr><td>Harici servis ortak arayüze uymuyor</td><td>Adapter</td></tr><tr><td>Mevcut davranışa ek sorumluluk gerekiyor</td><td>Decorator</td></tr><tr><td>Karmaşık alt sistem için basit giriş noktası gerekiyor</td><td>Facade</td></tr><tr><td>Algoritma çalışma anında değişiyor</td><td>Strategy</td></tr><tr><td>Olay birden fazla dinleyiciyi ilgilendiriyor</td><td>Observer</td></tr><tr><td>Nesne davranışı mevcut durumuna bağlı</td><td>State</td></tr><tr><td>İşlemleri kuyruklamak veya komut olarak saklamak gerekiyor</td><td>Command</td></tr><tr><td>İstek birden fazla işleyiciden geçiyor</td><td>Chain of Responsibility</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">Bu tabloyu kesin seçim kuralları olarak değerlendirmeyin. Aynı problem farklı bağlamlarda farklı çözümler gerektirebilir.</p>



<p class="wp-block-paragraph">Örneğin yalnızca iki seçenek içeren sabit bir algoritma için <code>switch</code> ifadesi yeterli olabilir. Yeni algoritmaların bağımsız ekipler tarafından geliştirildiği bir sistemde ise Strategy daha güçlü bir tercih hâline gelir.</p>



<h2 class="wp-block-heading">Spring Boot Projelerinde Tasarım Desenleri</h2>



<p class="wp-block-paragraph">Spring Framework, birçok tasarım deseninin fikirlerini kendi altyapısında kullanır.</p>



<p class="wp-block-paragraph">Bununla birlikte framework kullanmak, desenleri öğrenme ihtiyacını ortadan kaldırmaz.</p>



<h3 class="wp-block-heading">Dependency Injection ve Strategy</h3>



<p class="wp-block-paragraph">Spring, aynı arayüzün farklı implementasyonlarını bean olarak yönetebilir.</p>



<p class="wp-block-paragraph">Bir ödeme servisinde farklı <code>PaymentStrategy</code> sınıfları tanımlayıp uygun olanı çalışma anında seçebilirsiniz.</p>



<p class="wp-block-paragraph">Ancak seçimin nerede gerçekleşeceğini açıkça tasarlamalısınız. Bir servisin Spring ApplicationContext üzerinden sürekli bean araması, gizli bağımlılıklar yaratabilir.</p>



<p class="wp-block-paragraph">Constructor Injection çoğu durumda daha açık bir bağımlılık modeli sunar.</p>



<h3 class="wp-block-heading">Spring Event Modeli ve Observer</h3>



<p class="wp-block-paragraph">Spring&#8217;in <code>ApplicationEventPublisher</code> mekanizması, uygulama içinde olay yayınlama yaklaşımını destekler.</p>



<p class="wp-block-paragraph"><code>@EventListener</code> kullanan bileşenler ilgili olaylara tepki verebilir.</p>



<p class="wp-block-paragraph">Fakat uygulama içi olay yayınlamak ile dağıtık mesajlaşma arasında teslimat ve dayanıklılık açısından önemli farklar bulunur.</p>



<h3 class="wp-block-heading">Spring AOP ve Decorator</h3>



<p class="wp-block-paragraph">Spring AOP, belirli metot çağrılarına ek davranış bağlamayı kolaylaştırır.</p>



<p class="wp-block-paragraph">Transaction yönetimi, metrik toplama ve bazı loglama senaryoları buna örnek olabilir.</p>



<p class="wp-block-paragraph">Bu yapı klasik Decorator ile aynı uygulama tekniğini kullanmak zorunda değildir. Benzer sorumluluk ayrımı hedeflerine farklı mekanizmalarla ulaşabilir.</p>



<p class="wp-block-paragraph">Spring&#8217;in sunduğu soyutlamalar, tasarım kararının neden gerekli olduğunu anlamadığınızda karmaşıklığı gizleyebilir. Bu nedenle framework mekanizmasını seçmeden önce çözmek istediğiniz bağımlılık problemini tanımlayın.</p>



<h2 class="wp-block-heading">Tasarım Desenlerini Kullanırken Yapılan Yaygın Hatalar</h2>



<h3 class="wp-block-heading">Her Problem İçin Desen Aramak</h3>



<p class="wp-block-paragraph">Bir metot üç satırda anlaşılır biçimde çalışıyorsa onu beş sınıfa bölmek kod kalitesini otomatik olarak artırmaz.</p>



<p class="wp-block-paragraph">Soyutlama maliyetini karşılayacak somut bir değişiklik ihtiyacı olmalı.</p>



<h3 class="wp-block-heading">Gereksiz Singleton Kullanımı</h3>



<p class="wp-block-paragraph">Singleton, hızlı erişim sunduğu için cazip görünür. Ancak kontrolsüz global durum, özellikle paralel testlerde ve eşzamanlı işlemlerde sorun çıkarabilir.</p>



<h3 class="wp-block-heading">Factory ile Dependency Injection&#8217;ı Karıştırmak</h3>



<p class="wp-block-paragraph">Factory nesne oluşturma sorumluluğunu ele alır.</p>



<p class="wp-block-paragraph">Dependency Injection ise nesnenin ihtiyaç duyduğu bağımlılıkları dışarıdan almasına odaklanır.</p>



<p class="wp-block-paragraph">Bir uygulama her iki yaklaşımı birlikte kullanabilir.</p>



<h3 class="wp-block-heading">Observer ile Güvenilir Mesajlaşmayı Eşdeğer Görmek</h3>



<p class="wp-block-paragraph">Uygulama içindeki bir Observer mekanizması, olayları otomatik olarak kalıcı tutmaz.</p>



<p class="wp-block-paragraph">Bir listener hata verdiğinde veya uygulama kapandığında ne olacağını ayrıca tasarlamanız gerekir.</p>



<h3 class="wp-block-heading">Sırf Tasarım Deseni Kullanmak İçin Arayüz Açmak</h3>



<p class="wp-block-paragraph">Her somut sınıfın mutlaka bir arayüze ihtiyacı yoktur.</p>



<p class="wp-block-paragraph">Arayüz, gerçek bir sözleşmeyi veya anlamlı bir değişkenlik noktasını temsil ediyorsa değer kazandırır.</p>



<p class="wp-block-paragraph">Tek implementasyonu bulunan ve alternatif davranış ihtiyacı taşımayan bir sınıf için erken soyutlama gereksiz olabilir.</p>



<h2 class="wp-block-heading">Tasarım Deseni Seçerken Sorulması Gereken Sorular</h2>



<p class="wp-block-paragraph">Kodun içine yeni bir desen yerleştirmeden önce şu sorulara yanıt verin:</p>



<ol start="1" class="wp-block-list">
<li>Hangi somut problemle karşı karşıyayım?</li>



<li>Bu problem bugün gerçekten var mı, yoksa geleceğe ilişkin bir tahmin mi?</li>



<li>Mevcut kod değişikliklere hangi noktada direnç gösteriyor?</li>



<li>Daha basit bir Java dil özelliği aynı sorunu çözebilir mi?</li>



<li>Yeni soyutlama birim testleri kolaylaştıracak mı?</li>



<li>Bu tasarımı altı ay sonra okuyacak geliştirici niyetimi anlayabilecek mi?</li>



<li>Deseni kaldırırsam kod daha basit ve anlaşılır hâle gelir mi?</li>
</ol>



<p class="wp-block-paragraph">Bu sorular özellikle refactoring sırasında önemlidir.</p>



<p class="wp-block-paragraph">Refactoring sürecinde önce değişiklik maliyetinin yüksek olduğu bölgeyi bulun. Sonra yeni yapının gerçekten bu maliyeti azaltıp azaltmadığını testlerle doğrulayın.</p>



<h2 class="wp-block-heading">Sık Sorulan Sorular</h2>



<h3 class="wp-block-heading">Java&#8217;da en çok kullanılan tasarım desenleri hangileridir?</h3>



<p class="wp-block-paragraph">Strategy, Factory, Builder, Adapter, Observer ve Decorator, Java backend uygulamalarında sık karşılaşılan desenler arasındadır. Ancak kullanım sıklığı framework&#8217;e, mimariye ve projenin ihtiyaçlarına göre değişir.</p>



<h3 class="wp-block-heading">Java&#8217;da tasarım deseni kullanmak zorunlu mu?</h3>



<p class="wp-block-paragraph">Hayır. Tasarım desenleri dilin zorunlu bileşenleri değildir. Geliştirici, kodun bakımını ve genişletilmesini kolaylaştırdığı durumlarda bu çözümleri tercih eder.</p>



<h3 class="wp-block-heading">Factory ile Builder arasındaki fark nedir?</h3>



<p class="wp-block-paragraph">Factory hangi nesnenin oluşturulacağını veya nesne oluşturma sürecinin hangi bileşen tarafından yönetileceğini ele alır. Builder ise özellikle çok adımlı ve çok parametreli nesne oluşturma süreçlerine odaklanır.</p>



<h3 class="wp-block-heading">Strategy ile State arasındaki fark nedir?</h3>



<p class="wp-block-paragraph">Strategy, aynı amaca yönelik farklı algoritmalar arasında seçim yapmayı hedefler.</p>



<p class="wp-block-paragraph">State ise bir nesnenin kendi yaşam döngüsündeki mevcut durumuna bağlı davranışları yönetir.</p>



<p class="wp-block-paragraph">İki desen benzer sınıf yapıları kullanabilir; ancak tasarım niyetleri farklıdır.</p>



<h3 class="wp-block-heading">Java Record, Builder kullanımını gereksiz kılar mı?</h3>



<p class="wp-block-paragraph">Hayır. Record, veri odaklı sınıflarda tekrarlayan kodu azaltır. Builder ise karmaşık oluşturma süreçlerini ve çok sayıdaki isteğe bağlı parametreyi yönetmeye devam edebilir.</p>



<h3 class="wp-block-heading">Java 21 ile klasik tasarım desenleri hâlâ gerekli mi?</h3>



<p class="wp-block-paragraph">Evet. Java 21&#8217;in sealed türleri ve pattern matching özellikleri bazı çözümleri sadeleştirir. Ancak nesnelerin sorumluluklarını ayırmak, bağımlılıkları yönetmek ve değişebilir davranışları modellemek hâlâ tasarım kararları gerektirir.</p>



<h2 class="wp-block-heading">Sonuç: İyi Tasarım, Doğru Soyutlamayı Seçmekle Başlar</h2>



<p class="wp-block-paragraph">Java tasarım desenleri, geliştiriciye her problem için uygulanacak sabit kurallar sunmaz. Tekrar eden tasarım sorunlarını çözmek için ortak bir düşünme biçimi kazandırır.</p>



<p class="wp-block-paragraph">Singleton nesne yaşam döngüsüne, Factory nesne oluşturma kararlarına, Builder karmaşık yapılandırmalara odaklanır. Adapter ve Decorator bileşenlerin birlikte çalışma biçimini düzenler. Strategy, Observer ve State ise davranışların nasıl değişeceğini ve bileşenlerin nasıl iletişim kuracağını ele alır.</p>



<p class="wp-block-paragraph">Java 17 ve Java 21, bu kararları uygularken daha güçlü dil araçları sunuyor. Ancak kısa kod her zaman iyi tasarım anlamına gelmez; daha fazla sınıf da otomatik olarak daha iyi mimari oluşturmaz.</p>



<p class="wp-block-paragraph">Bir deseni seçmeden önce kodun hangi nedenle değiştiğini, hangi bağımlılıkların sorun çıkardığını ve ekleyeceğiniz soyutlamanın gerçek faydasını değerlendirin.</p>



<p class="wp-block-paragraph"><strong>İyi bir tasarım deseni, kodu karmaşık göstermek için değil, değişikliği daha kolay yönetmek için vardır.</strong></p>



<h2 class="wp-block-heading">Kaynaklar ve İleri Okuma</h2>



<ol start="1" class="wp-block-list">
<li>Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides — <em>Design Patterns: Elements of Reusable Object-Oriented Software</em>, Addison-Wesley, 1994.</li>



<li>Joshua Bloch — <em>Effective Java</em>, Third Edition, Addison-Wesley, 2018.</li>



<li>Robert C. Martin — <em>Clean Architecture</em>, Prentice Hall, 2017.</li>



<li>Oracle — Java SE 21 Language Documentation: <a href="https://docs.oracle.com/en/java/javase/21/language/">https://docs.oracle.com/en/java/javase/21/language/</a></li>



<li>OpenJDK — JEP 395, Records: <a href="https://openjdk.org/jeps/395">https://openjdk.org/jeps/395</a></li>



<li>OpenJDK — JEP 409, Sealed Classes: <a href="https://openjdk.org/jeps/409">https://openjdk.org/jeps/409</a></li>



<li>OpenJDK — JEP 441, Pattern Matching for switch: <a href="https://openjdk.org/jeps/441">https://openjdk.org/jeps/441</a></li>



<li>Refactoring.Guru — Design Patterns in Java: <a href="https://refactoring.guru/design-patterns/java">https://refactoring.guru/design-patterns/java</a></li>
</ol>



<p class="wp-block-paragraph"></p>
<p><a href="https://alikarahisar.com/blog/java-tasarim-desenleri/">Java Tasarım Desenleri (Design Patterns): Gerçek Hayat Örnekleri ve Modern Java</a> yazısı ilk önce <a href="https://alikarahisar.com/blog">Ali Karahisar</a> üzerinde ortaya çıktı.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://alikarahisar.com/blog/java-tasarim-desenleri/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Agile Proje Yönetimi ve Mühendislik Pratikleri: Başarılı Yazılım Ekipleri Nasıl Çalışır?</title>
		<link>https://alikarahisar.com/blog/agile-metodolojileri-agile-muhendislik-pratikleri-ve-yazilim-proje-yonetimi/</link>
					<comments>https://alikarahisar.com/blog/agile-metodolojileri-agile-muhendislik-pratikleri-ve-yazilim-proje-yonetimi/#respond</comments>
		
		<dc:creator><![CDATA[Ali Karahisar]]></dc:creator>
		<pubDate>Thu, 01 Oct 2026 20:03:29 +0000</pubDate>
				<category><![CDATA[Genel]]></category>
		<category><![CDATA[Yazılım]]></category>
		<category><![CDATA[Agile]]></category>
		<category><![CDATA[CI/CD]]></category>
		<category><![CDATA[Code Review]]></category>
		<category><![CDATA[Continuous Integration]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[DevSecOps]]></category>
		<category><![CDATA[Proje Yönetimi]]></category>
		<category><![CDATA[Scrum]]></category>
		<category><![CDATA[Teknik Borç]]></category>
		<category><![CDATA[Yazılım Mühendisliği]]></category>
		<guid isPermaLink="false">https://alikarahisar.com/blog/?p=73</guid>

					<description><![CDATA[<p>Yazılım ekipleri artık yalnızca hızlı kod yazarak başarılı olamıyor. Ekiplerin değişen gereksinimlere cevap vermesi, kaliteli yazılım üretmesi, teknik borcu kontrol altında tutması ve müşteriye düzenli olarak değer sunması gerekiyor. Bu noktada Agile proje yönetimi ve güçlü mühendislik pratikleri birlikte devreye giriyor. Agile yaklaşım, ekiplere değişime uyum sağlama yeteneği kazandırır. Ancak bir ekip yalnızca Scrum toplantıları [&#8230;]</p>
<p><a href="https://alikarahisar.com/blog/agile-metodolojileri-agile-muhendislik-pratikleri-ve-yazilim-proje-yonetimi/">Agile Proje Yönetimi ve Mühendislik Pratikleri: Başarılı Yazılım Ekipleri Nasıl Çalışır?</a> yazısı ilk önce <a href="https://alikarahisar.com/blog">Ali Karahisar</a> üzerinde ortaya çıktı.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Yazılım ekipleri artık yalnızca hızlı kod yazarak başarılı olamıyor. Ekiplerin değişen gereksinimlere cevap vermesi, kaliteli yazılım üretmesi, teknik borcu kontrol altında tutması ve müşteriye düzenli olarak değer sunması gerekiyor. Bu noktada <strong>Agile proje yönetimi</strong> ve güçlü <strong>mühendislik pratikleri</strong> birlikte devreye giriyor.</p>



<span id="more-73"></span>



<p class="wp-block-paragraph">Agile yaklaşım, ekiplere değişime uyum sağlama yeteneği kazandırır. Ancak bir ekip yalnızca Scrum toplantıları düzenleyerek veya Jira üzerinde sprint oluşturarak çevik hale gelmez. Ekip aynı zamanda kod kalitesini, test otomasyonunu, sürekli entegrasyonu, kod incelemeyi, gözlemlenebilirliği ve teknik borç yönetimini günlük çalışma biçiminin bir parçası haline getirmelidir.</p>



<p class="wp-block-paragraph">Başka bir ifadeyle Agile, yalnızca proje yönetimi metodolojisi değildir. Agile yaklaşım, ürün geliştirme kültürünü ve mühendislik disiplinini birlikte şekillendirir.</p>



<p class="wp-block-paragraph">Bu yazıda <strong>Agile proje yönetimi ile yazılım mühendisliği pratiklerinin nasıl birlikte çalışması gerektiğini</strong>, ekiplerin hangi hataları yaptığını ve sürdürülebilir bir Agile geliştirme kültürünün nasıl kurulabileceğini detaylı şekilde inceleyeceğiz.</p>



<h2 class="wp-block-heading">Agile Proje Yönetimi Nedir?</h2>



<p class="wp-block-paragraph">Agile proje yönetimi, büyük projeleri uzun süreli ve katı planlarla yönetmek yerine işi küçük parçalara ayırmayı ve düzenli geri bildirimlerle ilerlemeyi hedefler.</p>



<p class="wp-block-paragraph">Geleneksel proje yönetiminde ekipler çoğunlukla şu akışı takip eder:</p>



<p class="wp-block-paragraph">Gereksinim analizi → Tasarım → Geliştirme → Test → Canlıya geçiş</p>



<p class="wp-block-paragraph">Bu yaklaşım bazı projelerde çalışabilir. Ancak yazılım projelerinde gereksinimler sıklıkla değişir. Kullanıcı beklentileri farklılaşır, teknik kısıtlar ortaya çıkar veya pazar koşulları yeni öncelikler doğurur.</p>



<p class="wp-block-paragraph">Agile yaklaşım ise ekibin kısa geri bildirim döngüleri oluşturmasını sağlar.</p>



<p class="wp-block-paragraph">Ekip:</p>



<ul class="wp-block-list">
<li>işi küçük parçalara böler,</li>



<li>öncelikli işleri önce geliştirir,</li>



<li>çalışan yazılım üretir,</li>



<li>kullanıcıdan geri bildirim toplar,</li>



<li>elde ettiği verilerle sonraki adımı belirler.</li>
</ul>



<p class="wp-block-paragraph">Böylece ekip aylar sonra büyük bir ürün teslim etmek yerine düzenli aralıklarla değer üretir.</p>



<h2 class="wp-block-heading">Agile Olmak Scrum Kullanmak Anlamına Gelmez</h2>



<p class="wp-block-paragraph">Yazılım sektöründeki en yaygın hatalardan biri Agile ile Scrum kavramlarını aynı şey olarak değerlendirmektir.</p>



<p class="wp-block-paragraph">Scrum, Agile prensiplerini uygulamak için kullanılan çerçevelerden biridir.</p>



<p class="wp-block-paragraph">Ancak bir ekip şu etkinlikleri gerçekleştiriyor olabilir:</p>



<ul class="wp-block-list">
<li>Daily Scrum,</li>



<li>Sprint Planning,</li>



<li>Sprint Review,</li>



<li>Retrospective,</li>



<li>Backlog Refinement.</li>
</ul>



<p class="wp-block-paragraph">Buna rağmen ekip hâlâ çevik olmayabilir.</p>



<p class="wp-block-paragraph">Örneğin yönetim ekibi bütün kararları önceden veriyorsa, geliştiriciler yalnızca kendilerine verilen görevleri uyguluyorsa ve ekip kullanıcı geri bildirimlerini dikkate almıyorsa yapılan Scrum toplantıları tek başına Agile kültürü oluşturmaz.</p>



<p class="wp-block-paragraph">Agile yaklaşımın merkezinde toplantılar değil, <strong>hızlı geri bildirim ve sürekli değer üretimi</strong> bulunur.</p>



<h2 class="wp-block-heading">Agile Proje Yönetimi Neden Mühendislik Pratiklerine İhtiyaç Duyar?</h2>



<p class="wp-block-paragraph">Bir ekip iki haftalık sprintler kullanabilir ve düzenli olarak backlog yönetebilir. Ancak ekip yeterli mühendislik disiplinine sahip değilse geliştirme hızı kısa sürede düşer.</p>



<p class="wp-block-paragraph">Örneğin ekip sürekli yeni özellik geliştiriyor fakat otomatik test yazmıyorsa hata sayısı artar.</p>



<p class="wp-block-paragraph">Kod inceleme süreci bulunmuyorsa kod kalitesi ekip üyelerine göre değişir.</p>



<p class="wp-block-paragraph">Continuous Integration kullanılmıyorsa entegrasyon problemleri büyür.</p>



<p class="wp-block-paragraph">Teknik borç kontrol edilmezse küçük değişiklikler bile zamanla büyük geliştirme maliyetlerine dönüşür.</p>



<p class="wp-block-paragraph">Bu nedenle Agile proje yönetimi ekiplerin <strong>ne geliştireceğini ve hangi öncelikle geliştireceğini</strong> yönetirken mühendislik pratikleri ekibin <strong>yazılımı nasıl sürdürülebilir şekilde geliştireceğini</strong> belirler.</p>



<p class="wp-block-paragraph">İki yaklaşım birlikte çalıştığında ekip hem hızlı hem de güvenilir yazılım üretebilir.</p>



<h2 class="wp-block-heading">Başarılı Agile Ekiplerinin Kullandığı Mühendislik Pratikleri</h2>



<h2 class="wp-block-heading">1. Küçük ve Yönetilebilir İş Parçaları Oluşturun</h2>



<p class="wp-block-paragraph">Agile geliştirme sürecinde büyük işler belirsizliği artırır.</p>



<p class="wp-block-paragraph">Örneğin ekip backlog&#8217;a şu şekilde bir iş ekleyebilir:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">Ödeme sistemini geliştir.</p>
</blockquote>



<p class="wp-block-paragraph">Bu iş oldukça geniştir. Analiz, geliştirme ve test sırasında birçok bilinmeyen ortaya çıkabilir.</p>



<p class="wp-block-paragraph">Bunun yerine ekip işi daha küçük parçalara bölebilir:</p>



<ul class="wp-block-list">
<li>kredi kartı ödeme ekranını oluştur,</li>



<li>ödeme doğrulama servisini geliştir,</li>



<li>başarısız ödeme akışını yönet,</li>



<li>ödeme sonucunu kullanıcıya göster,</li>



<li>işlem kayıtlarını oluştur.</li>
</ul>



<p class="wp-block-paragraph">Küçük işler ekiplerin daha doğru tahmin yapmasını ve daha hızlı geri bildirim almasını sağlar.</p>



<p class="wp-block-paragraph">Ayrıca küçük değişiklikler kod inceleme sürecini kolaylaştırır.</p>



<h2 class="wp-block-heading">2. Definition of Done Kullanın</h2>



<p class="wp-block-paragraph">Bir geliştiricinin “iş tamamlandı” demesi her zaman aynı anlama gelmez.</p>



<p class="wp-block-paragraph">Bir geliştirici kodu yazınca işi tamamlanmış kabul edebilir. Başka bir geliştirici testler tamamlandığında işin bittiğini düşünebilir.</p>



<p class="wp-block-paragraph">Bu farklılık ekip içinde belirsizlik yaratır.</p>



<p class="wp-block-paragraph">Bu nedenle ekip açık bir <strong>Definition of Done</strong> oluşturmalıdır.</p>



<p class="wp-block-paragraph">Örneğin bir iş ancak aşağıdaki koşullar gerçekleştiğinde tamamlanmış sayılabilir:</p>



<ul class="wp-block-list">
<li>geliştirme tamamlandı,</li>



<li>unit testler yazıldı,</li>



<li>gerekli integration testler çalışıyor,</li>



<li>kod incelemesi tamamlandı,</li>



<li>güvenlik kontrolleri geçti,</li>



<li>CI pipeline başarılı oldu,</li>



<li>gerekli dokümantasyon güncellendi,</li>



<li>acceptance criteria karşılandı.</li>
</ul>



<p class="wp-block-paragraph">Definition of Done kalite konusunda ortak bir ekip standardı oluşturur.</p>



<h2 class="wp-block-heading">3. Code Review Sürecini Standartlaştırın</h2>



<p class="wp-block-paragraph">Code review yalnızca hataları bulmak için kullanılmaz.</p>



<p class="wp-block-paragraph">İyi bir kod inceleme süreci ekip üyeleri arasında bilgi paylaşımını artırır ve ortak kod standartları oluşturur.</p>



<p class="wp-block-paragraph">Reviewer şu konuları değerlendirebilir:</p>



<ul class="wp-block-list">
<li>kod okunabilir mi?</li>



<li>gereksiz karmaşıklık var mı?</li>



<li>mevcut mimariye uygun mu?</li>



<li>güvenlik problemi oluşturuyor mu?</li>



<li>hata durumları yönetiliyor mu?</li>



<li>testler yeterli mi?</li>



<li>kod tekrarına neden oluyor mu?</li>
</ul>



<p class="wp-block-paragraph">Ancak code review sürecini bir onay mekanizmasına dönüştürmemek gerekir.</p>



<p class="wp-block-paragraph">Amaç geliştiriciyi denetlemek değil, ürünün teknik kalitesini artırmaktır.</p>



<h2 class="wp-block-heading">4. Continuous Integration Kullanın</h2>



<p class="wp-block-paragraph">Geliştiriciler kodlarını uzun süre ayrı branch&#8217;lerde tuttuğunda entegrasyon problemleri büyür.</p>



<p class="wp-block-paragraph">Bu nedenle ekip mümkün olduğunca sık entegrasyon yapmalıdır.</p>



<p class="wp-block-paragraph">Continuous Integration sürecinde geliştirici kodunu repository&#8217;ye gönderdiğinde otomatik süreçler çalışır.</p>



<p class="wp-block-paragraph">Örneğin pipeline:</p>



<ol start="1" class="wp-block-list">
<li>projeyi build eder,</li>



<li>unit testleri çalıştırır,</li>



<li>integration testleri çalıştırır,</li>



<li>static code analysis gerçekleştirir,</li>



<li>güvenlik kontrollerini çalıştırır,</li>



<li>gerekli kalite kontrollerini uygular.</li>
</ol>



<p class="wp-block-paragraph">Pipeline hata verirse ekip problemi hızlı şekilde çözer.</p>



<p class="wp-block-paragraph">Böylece ekip entegrasyon hatalarını geliştirme sürecinin sonunda değil, ortaya çıktıkları anda yakalar.</p>



<h2 class="wp-block-heading">5. Test Otomasyonuna Yatırım Yapın</h2>



<p class="wp-block-paragraph">Manuel test süreçları büyüyen yazılım projelerinde tek başına yeterli olmaz.</p>



<p class="wp-block-paragraph">Her yeni özellik mevcut fonksiyonları etkileyebilir.</p>



<p class="wp-block-paragraph">Bu nedenle ekip otomatik testlerden oluşan güçlü bir güvenlik ağı kurmalıdır.</p>



<p class="wp-block-paragraph">Ekip farklı seviyelerde test kullanabilir:</p>



<ul class="wp-block-list">
<li>Unit Test</li>



<li>Integration Test</li>



<li>API Test</li>



<li>Contract Test</li>



<li>End-to-End Test</li>
</ul>



<p class="wp-block-paragraph">Ancak ekip yalnızca test sayısını artırmayı hedeflememelidir.</p>



<p class="wp-block-paragraph">Testlerin gerçekten davranışı doğrulaması gerekir.</p>



<p class="wp-block-paragraph">Örneğin yüzlerce anlamsız unit test yüksek test coverage değeri üretebilir ancak gerçek hataları yakalamayabilir.</p>



<p class="wp-block-paragraph">Bu nedenle ekip coverage oranını tek başına kalite metriği olarak değerlendirmemelidir.</p>



<h2 class="wp-block-heading">6. Teknik Borcu Backlog İçinde Yönetin</h2>



<p class="wp-block-paragraph">Teknik borç yazılım projelerinde tamamen ortadan kaldırılamaz.</p>



<p class="wp-block-paragraph">Bazen ekip hızlı karar almak zorunda kalır. Bazen zaman baskısı nedeniyle ideal çözüm yerine daha basit bir çözüm uygular.</p>



<p class="wp-block-paragraph">Problem teknik borcun oluşması değildir.</p>



<p class="wp-block-paragraph">Problem teknik borcun görünmez hale gelmesidir.</p>



<p class="wp-block-paragraph">Ekip teknik borcu backlog içinde takip etmelidir.</p>



<p class="wp-block-paragraph">Örneğin:</p>



<ul class="wp-block-list">
<li>eski kütüphaneleri güncelle,</li>



<li>tekrar eden kodları refactor et,</li>



<li>yavaş çalışan sorguyu optimize et,</li>



<li>eski API endpoint&#8217;lerini kaldır,</li>



<li>kritik modül için otomatik test ekle.</li>
</ul>



<p class="wp-block-paragraph">Ürün yöneticisi ve mühendislik ekibi teknik borcun ürün üzerindeki etkisini birlikte değerlendirmelidir.</p>



<h2 class="wp-block-heading">7. Refactoring İçin Ayrı Proje Beklemeyin</h2>



<p class="wp-block-paragraph">Bazı ekipler aylar boyunca yeni özellik geliştirir ve daha sonra büyük bir “refactoring projesi” başlatmayı planlar.</p>



<p class="wp-block-paragraph">Bu yaklaşım genellikle başarısız olur.</p>



<p class="wp-block-paragraph">Çünkü ürün geliştirme hiçbir zaman tamamen durmaz.</p>



<p class="wp-block-paragraph">Bunun yerine ekip refactoring çalışmalarını günlük geliştirme sürecinin doğal bir parçası haline getirmelidir.</p>



<p class="wp-block-paragraph">Geliştirici bir modülde değişiklik yaparken gerektiğinde:</p>



<ul class="wp-block-list">
<li>karmaşık metotları sadeleştirebilir,</li>



<li>isimlendirmeleri düzeltebilir,</li>



<li>kod tekrarlarını azaltabilir,</li>



<li>testleri iyileştirebilir.</li>
</ul>



<p class="wp-block-paragraph">Küçük ve sürekli iyileştirmeler büyük refactoring projelerinin oluşmasını engeller.</p>



<h2 class="wp-block-heading">8. Trunk-Based Development Yaklaşımını Değerlendirin</h2>



<p class="wp-block-paragraph">Uzun yaşayan feature branch&#8217;ler entegrasyon riskini artırabilir.</p>



<p class="wp-block-paragraph">Branch haftalar boyunca açık kaldığında ana kod tabanı değişmeye devam eder. Daha sonra merge işlemi büyük çatışmalar oluşturabilir.</p>



<p class="wp-block-paragraph">Trunk-Based Development yaklaşımı geliştiricilerin küçük değişiklikleri sık şekilde ana geliştirme hattına entegre etmesini teşvik eder.</p>



<p class="wp-block-paragraph">Bu yaklaşımı desteklemek için ekip:</p>



<ul class="wp-block-list">
<li>küçük commitler oluşturabilir,</li>



<li>kısa yaşayan branch&#8217;ler kullanabilir,</li>



<li>feature flag mekanizması uygulayabilir,</li>



<li>güçlü CI pipeline kurabilir.</li>
</ul>



<p class="wp-block-paragraph">Böylece ekip büyük entegrasyon problemlerini azaltabilir.</p>



<h2 class="wp-block-heading">9. Feature Flag Kullanarak Deployment ile Release Sürecini Ayırın</h2>



<p class="wp-block-paragraph">Bir özelliğin production ortamına deploy edilmesi, kullanıcıların özelliği hemen kullanması gerektiği anlamına gelmez.</p>



<p class="wp-block-paragraph">Feature flag yaklaşımı deployment ile release kavramlarını birbirinden ayırır.</p>



<p class="wp-block-paragraph">Ekip yeni özelliği production ortamına gönderebilir ancak özelliği kapalı tutabilir.</p>



<p class="wp-block-paragraph">Daha sonra özelliği:</p>



<ul class="wp-block-list">
<li>belirli kullanıcılara,</li>



<li>belirli müşteri gruplarına,</li>



<li>belirli ülkelere,</li>



<li>küçük bir kullanıcı yüzdesine</li>
</ul>



<p class="wp-block-paragraph">açabilir.</p>



<p class="wp-block-paragraph">Bu yaklaşım kontrollü yayın yapmayı kolaylaştırır ve operasyonel riski azaltır.</p>



<h2 class="wp-block-heading">10. Observability Sistemlerini Geliştirme Sürecine Dahil Edin</h2>



<p class="wp-block-paragraph">Bir özelliğin production ortamına çıkması geliştirme sürecinin sonu değildir.</p>



<p class="wp-block-paragraph">Ekip uygulamanın gerçek ortamda nasıl davrandığını takip etmelidir.</p>



<p class="wp-block-paragraph">Bu nedenle ekip üç temel gözlemlenebilirlik kaynağını kullanabilir:</p>



<ul class="wp-block-list">
<li>Logs</li>



<li>Metrics</li>



<li>Traces</li>
</ul>



<p class="wp-block-paragraph">Bu veriler sayesinde ekip yalnızca “sistem çalışıyor mu?” sorusunu değil, “sistem neden bu şekilde davranıyor?” sorusunu da cevaplayabilir.</p>



<p class="wp-block-paragraph">Örneğin bir API&#8217;nin cevap süresi yükseldiğinde ekip metric verileriyle problemi tespit edebilir, tracing ile çağrı zincirini inceleyebilir ve log kayıtlarıyla kök nedeni araştırabilir.</p>



<h2 class="wp-block-heading">11. Güvenliği Sprint Sonuna Bırakmayın</h2>



<p class="wp-block-paragraph">Güvenlik kontrollerini geliştirme bittikten sonra gerçekleştirmek hem maliyeti hem de riski artırır.</p>



<p class="wp-block-paragraph">Bu nedenle ekip güvenliği geliştirme yaşam döngüsünün başından itibaren ele almalıdır.</p>



<p class="wp-block-paragraph">DevSecOps yaklaşımı bu noktada önemli avantaj sağlar.</p>



<p class="wp-block-paragraph">CI/CD pipeline içinde örneğin şu kontroller çalışabilir:</p>



<ul class="wp-block-list">
<li>SAST,</li>



<li>dependency scanning,</li>



<li>secret scanning,</li>



<li>container image scanning,</li>



<li>IaC security scanning.</li>
</ul>



<p class="wp-block-paragraph">Bunun yanında ekip threat modeling ve secure coding pratiklerini geliştirme sürecine dahil edebilir.</p>



<p class="wp-block-paragraph">Güvenlik ekibi yalnızca release öncesinde kontrol yapan bir kapı görevi görmemelidir. Güvenlik uzmanları yazılım ekipleriyle birlikte çalışmalıdır.</p>



<h2 class="wp-block-heading">Agile Takımlarında Product Owner ve Teknik Ekibin İlişkisi</h2>



<p class="wp-block-paragraph">Başarılı Agile ekiplerinde ürün ve mühendislik birbirinden kopuk çalışmaz.</p>



<p class="wp-block-paragraph">Product Owner genellikle şu konulara odaklanır:</p>



<ul class="wp-block-list">
<li>müşteri ihtiyacı,</li>



<li>ürün değeri,</li>



<li>iş öncelikleri,</li>



<li>ürün hedefleri.</li>
</ul>



<p class="wp-block-paragraph">Mühendislik ekibi ise şu konulara odaklanır:</p>



<ul class="wp-block-list">
<li>teknik tasarım,</li>



<li>mimari,</li>



<li>performans,</li>



<li>güvenlik,</li>



<li>bakım maliyeti,</li>



<li>teknik borç.</li>
</ul>



<p class="wp-block-paragraph">Ancak iki tarafın tamamen bağımsız karar vermesi doğru sonuç üretmez.</p>



<p class="wp-block-paragraph">Örneğin Product Owner yalnızca özellik geliştirmeye odaklanır ve teknik borcu sürekli ertelerse geliştirme hızı zamanla düşebilir.</p>



<p class="wp-block-paragraph">Aynı şekilde mühendislik ekibi iş değerini dikkate almadan sürekli teknik mükemmellik peşinde koşarsa ürün geliştirme maliyeti artabilir.</p>



<p class="wp-block-paragraph">Bu nedenle ekip iş değeri ile teknik sürdürülebilirlik arasında denge kurmalıdır.</p>



<h2 class="wp-block-heading">Agile Takımlarında Velocity Nasıl Kullanılmalı?</h2>



<p class="wp-block-paragraph">Velocity, bir ekibin sprint içinde tamamladığı iş miktarını anlamaya yardımcı olabilir.</p>



<p class="wp-block-paragraph">Ancak yöneticiler velocity değerini performans metriğine dönüştürdüğünde ciddi problemler ortaya çıkar.</p>



<p class="wp-block-paragraph">Örneğin:</p>



<p class="wp-block-paragraph">Takım A: 40 story point</p>



<p class="wp-block-paragraph">Takım B: 60 story point</p>



<p class="wp-block-paragraph">Bu değerlerden yola çıkarak Takım B&#8217;nin daha üretken olduğunu söyleyemezsiniz.</p>



<p class="wp-block-paragraph">Her takım story point değerlerini kendi bağlamında belirler.</p>



<p class="wp-block-paragraph">Daha önemlisi ekip velocity değerinin hedef olduğunu düşündüğünde puanları yükseltmeye başlayabilir.</p>



<p class="wp-block-paragraph">Sonuç olarak metrik anlamını kaybeder.</p>



<p class="wp-block-paragraph">Velocity ekiplerin kapasite planlamasına yardımcı olabilir. Ancak bireysel veya ekip performansını ölçmek için kullanılmamalıdır.</p>



<h2 class="wp-block-heading">Agile Projelerde Ölçülmesi Gereken Metrikler</h2>



<p class="wp-block-paragraph">Yazılım ekipleri yalnızca “kaç story tamamladık?” sorusuna odaklanmamalıdır.</p>



<p class="wp-block-paragraph">Daha dengeli metrikler kullanmak gerekir.</p>



<p class="wp-block-paragraph">Örneğin ekip şu göstergeleri takip edebilir:</p>



<h3 class="wp-block-heading">Lead Time</h3>



<p class="wp-block-paragraph">Bir ihtiyacın ortaya çıkmasından kullanıcıya ulaşmasına kadar geçen süreyi gösterir.</p>



<h3 class="wp-block-heading">Cycle Time</h3>



<p class="wp-block-paragraph">Ekibin bir iş üzerinde aktif olarak çalışmaya başlamasından işi tamamlamasına kadar geçen süreyi ölçer.</p>



<h3 class="wp-block-heading">Deployment Frequency</h3>



<p class="wp-block-paragraph">Ekibin production ortamına ne sıklıkla deployment yaptığını gösterir.</p>



<h3 class="wp-block-heading">Change Failure Rate</h3>



<p class="wp-block-paragraph">Production ortamına yapılan değişikliklerin ne kadarının hata veya incident oluşturduğunu ölçmeye yardımcı olur.</p>



<h3 class="wp-block-heading">Mean Time to Restore</h3>



<p class="wp-block-paragraph">Bir problem oluştuğunda sistemin ne kadar hızlı normale döndüğünü anlamaya yardımcı olur.</p>



<h3 class="wp-block-heading">Defect Escape Rate</h3>



<p class="wp-block-paragraph">Test süreçlerinden geçerek production ortamına ulaşan hataları takip etmeyi sağlar.</p>



<p class="wp-block-paragraph">Bu metrikler birlikte değerlendirildiğinde ekip hem hız hem de kalite konusunda daha anlamlı sonuçlar elde eder.</p>



<h2 class="wp-block-heading">Agile Takımlarının En Sık Yaptığı Hatalar</h2>



<h3 class="wp-block-heading">Agile&#8217;ı Toplantı Sistemine Dönüştürmek</h3>



<p class="wp-block-paragraph">Daily yapmak ekibi otomatik olarak Agile yapmaz.</p>



<p class="wp-block-paragraph">Toplantılar iletişim aracıdır. Amaç değildir.</p>



<p class="wp-block-paragraph">Her etkinlik ekip için gerçek bir değer üretmelidir.</p>



<h3 class="wp-block-heading">Sprint İçinde Sürekli Yeni İş Eklemek</h3>



<p class="wp-block-paragraph">Sprint başladıktan sonra sürekli yeni işler eklemek ekip odağını bozar.</p>



<p class="wp-block-paragraph">Gerçek acil durumlar dışında ekip planlanan sprint hedefini korumalıdır.</p>



<h3 class="wp-block-heading">Teknik Borcu Sürekli Ertelemek</h3>



<p class="wp-block-paragraph">Her sprint yalnızca yeni özellik geliştirmek kısa vadede hızlı görünür.</p>



<p class="wp-block-paragraph">Ancak teknik borç zamanla geliştirme hızını ciddi şekilde düşürebilir.</p>



<h3 class="wp-block-heading">Geliştiricileri Story Point ile Karşılaştırmak</h3>



<p class="wp-block-paragraph">Story point bireysel performans metriği değildir.</p>



<p class="wp-block-paragraph">Bir geliştiricinin 20 point, diğerinin 10 point tamamlaması ilk geliştiricinin iki kat üretken olduğunu göstermez.</p>



<h3 class="wp-block-heading">Her Sorunu Yeni Bir Toplantıyla Çözmeye Çalışmak</h3>



<p class="wp-block-paragraph">Agile ekiplerin iletişim kurmasını teşvik eder fakat gereksiz toplantılar geliştirme zamanını azaltır.</p>



<p class="wp-block-paragraph">Asenkron iletişim birçok durumda daha verimli olabilir.</p>



<h3 class="wp-block-heading">Sprint&#8217;i Mini Waterfall&#8217;a Dönüştürmek</h3>



<p class="wp-block-paragraph">Bazı ekipler iki haftalık sprinti şu şekilde kullanır:</p>



<p class="wp-block-paragraph">İlk hafta analiz ve geliştirme.</p>



<p class="wp-block-paragraph">İkinci hafta test.</p>



<p class="wp-block-paragraph">Bu yaklaşım sprint içinde küçük bir Waterfall modeli oluşturur.</p>



<p class="wp-block-paragraph">Bunun yerine analiz, geliştirme ve test faaliyetleri sprint boyunca birlikte ilerlemelidir.</p>



<h2 class="wp-block-heading">Etkili Bir Agile Yazılım Ekibi Nasıl Kurulur?</h2>



<p class="wp-block-paragraph">Güçlü bir Agile kültür oluşturmak için yalnızca süreçleri değiştirmek yeterli değildir.</p>



<p class="wp-block-paragraph">Ekip çalışma biçimini de değiştirmelidir.</p>



<p class="wp-block-paragraph">Başlangıç için şu yaklaşım uygulanabilir:</p>



<ol start="1" class="wp-block-list">
<li>Ürün hedeflerini netleştirin.</li>



<li>Backlog&#8217;u iş değerine göre önceliklendirin.</li>



<li>Büyük işleri küçük parçalara bölün.</li>



<li>Definition of Done oluşturun.</li>



<li>Code review standardı belirleyin.</li>



<li>CI pipeline kurun.</li>



<li>Test otomasyonunu geliştirin.</li>



<li>Teknik borcu backlog içinde görünür hale getirin.</li>



<li>Deployment süreçlerini otomatikleştirin.</li>



<li>Production metriklerini takip edin.</li>



<li>Retrospective toplantılarında gerçek aksiyonlar belirleyin.</li>



<li>Belirlenen aksiyonların uygulanıp uygulanmadığını takip edin.</li>
</ol>



<p class="wp-block-paragraph">Burada en kritik nokta sürekli iyileştirmedir.</p>



<p class="wp-block-paragraph">Agile dönüşüm bir proje değildir.</p>



<p class="wp-block-paragraph">“Agile&#8217;a geçtik ve dönüşüm tamamlandı” yaklaşımı Agile felsefesine ters düşer.</p>



<p class="wp-block-paragraph">Ekipler çalışma biçimlerini sürekli değerlendirmeli ve geliştirmelidir.</p>



<h2 class="wp-block-heading">Agile Kültüründe Retrospective Neden Önemlidir?</h2>



<p class="wp-block-paragraph">Retrospective toplantısı birçok ekipte şu üç sorudan oluşur:</p>



<ul class="wp-block-list">
<li>Ne iyi gitti?</li>



<li>Ne kötü gitti?</li>



<li>Neyi geliştirebiliriz?</li>
</ul>



<p class="wp-block-paragraph">Ancak ekip yalnızca problemleri konuşuyor ve hiçbir aksiyon almıyorsa toplantı zamanla değerini kaybeder.</p>



<p class="wp-block-paragraph">İyi bir retrospective sonunda ekip az sayıda fakat uygulanabilir aksiyon belirlemelidir.</p>



<p class="wp-block-paragraph">Örneğin:</p>



<p class="wp-block-paragraph"><strong>Problem:</strong> Pull Request&#8217;ler uzun süre bekliyor.</p>



<p class="wp-block-paragraph"><strong>Aksiyon:</strong> Takım üyeleri günde iki kez açık Pull Request&#8217;leri kontrol edecek.</p>



<p class="wp-block-paragraph">Bir sonraki retrospective toplantısında ekip bu aksiyonun işe yarayıp yaramadığını değerlendirebilir.</p>



<p class="wp-block-paragraph">Bu yaklaşım retrospective toplantısını şikâyet toplantısından çıkararak sürekli iyileştirme mekanizmasına dönüştürür.</p>



<h2 class="wp-block-heading">Agile Proje Yönetiminde Teknik Liderin Rolü</h2>



<p class="wp-block-paragraph">Teknik lider Agile ekip içinde yalnızca mimari karar veren kişi olmamalıdır.</p>



<p class="wp-block-paragraph">İyi bir teknik lider:</p>



<ul class="wp-block-list">
<li>ekip üyelerinin karar süreçlerine katılmasını sağlar,</li>



<li>teknik riskleri görünür hale getirir,</li>



<li>mühendislik standartlarını geliştirir,</li>



<li>teknik borcu yönetir,</li>



<li>bilgi paylaşımını destekler,</li>



<li>karmaşık problemlerde ekibe rehberlik eder.</li>
</ul>



<p class="wp-block-paragraph">Bununla birlikte teknik lider bütün kararları tek başına almamalıdır.</p>



<p class="wp-block-paragraph">Ekibin teknik karar alma yeteneğini geliştirmek uzun vadede çok daha sürdürülebilir sonuç üretir.</p>



<h2 class="wp-block-heading">Sonuç: Agile ile Mühendislik Disiplinini Birlikte Yönetin</h2>



<p class="wp-block-paragraph">Agile proje yönetimi ekiplerin değişime hızlı cevap vermesini ve müşteriye daha sık değer sunmasını sağlar. Ancak güçlü mühendislik pratikleri olmadan bu hız sürdürülebilir hale gelmez.</p>



<p class="wp-block-paragraph">Başarılı yazılım ekipleri yalnızca sprint planlamaz.</p>



<p class="wp-block-paragraph">Aynı zamanda:</p>



<ul class="wp-block-list">
<li>küçük değişiklikler geliştirir,</li>



<li>kod incelemesi yapar,</li>



<li>otomatik testler oluşturur,</li>



<li>sürekli entegrasyon uygular,</li>



<li>teknik borcu yönetir,</li>



<li>güvenliği geliştirme sürecine dahil eder,</li>



<li>production sistemlerini gözlemler,</li>



<li>geri bildirimlerden öğrenir.</li>
</ul>



<p class="wp-block-paragraph">Dolayısıyla gerçek Agile yaklaşımı, proje yönetimi ile yazılım mühendisliğini birbirinden ayırmaz.</p>



<p class="wp-block-paragraph">Agile süreç ekiplerin <strong>doğru işi geliştirmesine</strong>, mühendislik pratikleri ise bu işi <strong>doğru şekilde geliştirmesine</strong> yardımcı olur.</p>



<p class="wp-block-paragraph">Bu iki disiplin birlikte çalıştığında ekip yalnızca daha hızlı yazılım üretmez. Aynı zamanda daha kaliteli, sürdürülebilir ve değişime daha dayanıklı ürünler geliştirir.</p>
<p><a href="https://alikarahisar.com/blog/agile-metodolojileri-agile-muhendislik-pratikleri-ve-yazilim-proje-yonetimi/">Agile Proje Yönetimi ve Mühendislik Pratikleri: Başarılı Yazılım Ekipleri Nasıl Çalışır?</a> yazısı ilk önce <a href="https://alikarahisar.com/blog">Ali Karahisar</a> üzerinde ortaya çıktı.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://alikarahisar.com/blog/agile-metodolojileri-agile-muhendislik-pratikleri-ve-yazilim-proje-yonetimi/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Hoş geldin CachyOS, Güle güle Windows</title>
		<link>https://alikarahisar.com/blog/gule-gule-windows-hos-geldin-cachyos/</link>
					<comments>https://alikarahisar.com/blog/gule-gule-windows-hos-geldin-cachyos/#respond</comments>
		
		<dc:creator><![CDATA[Ali Karahisar]]></dc:creator>
		<pubDate>Sat, 26 Sep 2026 22:24:45 +0000</pubDate>
				<category><![CDATA[Genel]]></category>
		<category><![CDATA[Hayatın içinden]]></category>
		<category><![CDATA[GNU/Linux]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Pardus]]></category>
		<category><![CDATA[Pisi Linux]]></category>
		<guid isPermaLink="false">https://alikarahisar.com/blog/?p=64</guid>

					<description><![CDATA[<p>Bu yazıyı yazmaya başladığım günden 195 gün önce hayatımdan çıkardım Windows&#8217;u. Hatta Reddit gibi platformlardaki tabiriyle Winslop denen işletme (işletim sistemi tabirini bilerek kullanmadım) sistemini. Sanal makine üzerinden uzun yıllar deneyimin olsa da hiç bir zaman tam anlamıyla ana işletim sistemi olarak kullanmadığım GNU/Linux ailesinden bir işletim sistemi olan Cachy OS&#8216;i masaüstü bilgisayarıma kurdum. Açıkcası [&#8230;]</p>
<p><a href="https://alikarahisar.com/blog/gule-gule-windows-hos-geldin-cachyos/">Hoş geldin CachyOS, Güle güle Windows</a> yazısı ilk önce <a href="https://alikarahisar.com/blog">Ali Karahisar</a> üzerinde ortaya çıktı.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Bu yazıyı yazmaya başladığım günden 195 gün önce hayatımdan çıkardım Windows&#8217;u. Hatta Reddit gibi platformlardaki tabiriyle Winslop denen işletme (işletim sistemi tabirini bilerek kullanmadım) sistemini. Sanal makine üzerinden uzun yıllar deneyimin olsa da hiç bir zaman tam anlamıyla ana işletim sistemi olarak kullanmadığım <a href="https://www.gnu.org/gnu/linux-and-gnu.tr.html" data-type="link" data-id="https://www.gnu.org/gnu/linux-and-gnu.tr.html" target="_blank" rel="noreferrer noopener">GNU/Linux</a> ailesinden bir işletim sistemi olan <a href="https://cachyos.org/" data-type="link" data-id="https://cachyos.org/" target="_blank" rel="noreferrer noopener">Cachy OS</a>&#8216;i masaüstü bilgisayarıma kurdum.</p>



<span id="more-64"></span>



<figure class="wp-block-image aligncenter size-large is-style-default"><img decoding="async" src="https://cachyos.org/_astro/logo.DuMERIP6.svg" alt="Cachy-Os-Logo"/></figure>



<p class="wp-block-paragraph">Açıkcası idle konumda olan bir bilgisayarın 6GB seviyelerinde RAM kullanımı benim için can sıkıcı bir durum. Üstelik tamamen debloat etmiş olmama rağmen bunu yaşamak baya canımı sıktı. Zaten debloat etmeden önce de arka planda toplanıp en uygun zamanda gönderilen telemetry verilerini yazmıyorum bile. Her ne kadar geri çekseler de <a href="https://en.wikipedia.org/wiki/Windows_Recall" data-type="link" data-id="https://en.wikipedia.org/wiki/Windows_Recall" target="_blank" rel="noreferrer noopener">Windows ReCall</a> gibi bir ürün, kullanıcı için mide bulandırıcı.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">Windows Recall, kullanıcının masaüstünün ekran görüntüsünü birkaç saniye aralıklarla alır ve ardından cihaz üzerinde çalışan büyük dil modellerini kullanarak kullanıcının daha önce ekranında bulunan öğeleri ve bilgileri geri getirmesini sağlar. Bu özellik, Mayıs 2024’te Microsoft tarafından, GPT-4o’nun Microsoft Copilot’a entegrasyonu ve Windows 11’deki yenilenmiş kullanıcı arayüzüyle birlikte duyuruldu. &#8211; Wikipedia</p>
</blockquote>



<h2 class="wp-block-heading">CachyOS Seçme Sebebim Neydi?</h2>



<p class="wp-block-paragraph">Arch Linux tabanlı olması ama Arch&#8217;a göre daha kolay kurulması bu sebeplerin başında geliyor. İkinci sebep ise oyun desteği. Bu başlı başına CachyOs&#8217;in bir özellliği olmasa da paket işlemlerini kurulumdan sonra kolayca halledebilmek cezbedici bir konuma koyuyor kendisini.</p>



<p class="wp-block-paragraph">Peki asıl sebep oyun mu; tabii ki hayır. CachyOs güncel işlemcilere özel olarak derleniyor bu da performans konusunda bize ekstra bir avantaj sağlıyor.</p>



<p class="wp-block-paragraph">Şu ana kadar da bahsi geçen diğer işletim sisteminin eksikliğini de gram hissetmedim. Ama özellikle oyun konusunda bir not da düşmek lazım. Benim oynadığım oyunların hiç birinde bir sorun yaşamadım. Bu oyunlara Microsoft Studios oyunu olan Age Of Empires 4&#8217;de dahil. Ama kernel based anti cheat kullanan bir oyun oynuyorsanız onları oynayamayacağınızı söyleyeyim.</p>



<p class="wp-block-paragraph">Seçim sebeplerimin bazıları ise teknik olduğundan dolayı onları burada yazmak istemiyorum. Genel hatları ile yukarıda yazdığım sebepler diyebiliriz.</p>



<h2 class="wp-block-heading">GNU/Linux&#8217;a geçeceğim, hangi dağıtımı kullanmalıyım?</h2>



<p class="wp-block-paragraph">Bu tamamen sizin kullanım amacınıza bağlı bir seçim. Türkiye&#8217;den çıkmış bir distro yani dağıtım kullanmak için <a href="https://distrowatch.com/search.php?origin=T%C3%BCrkiye#simple" data-type="link" data-id="https://distrowatch.com/search.php?origin=T%C3%BCrkiye#simple" target="_blank" rel="noreferrer noopener">buradaki</a> dağıtımları inceleyebilirsiniz. Benim önerim ise <a href="https://pardus.org.tr/" data-type="link" data-id="https://pardus.org.tr/" target="_blank" rel="noreferrer noopener">Pardus</a> ve <a href="https://pisilinux.org/" data-type="link" data-id="https://pisilinux.org/" target="_blank" rel="noreferrer noopener">Pisi Linux</a> olacaktır. Yukarıda bağlantısını verdiğim DistroWatch üzerinden de incelemeler yapabilirsiniz.</p>



<p class="wp-block-paragraph">Eğer ben hızlıca isteklerimi seçeyim de bana bir dağıtım önersin diyorsanız; Distro Pusulasına bakabilirsiniz.</p>



<h2 class="wp-block-heading">Distro Pusulası Nedir?</h2>



<p class="wp-block-paragraph"><a href="https://alikarahisar.com/linux-distros/" data-type="link" data-id="https://alikarahisar.com/linux-distros/" target="_blank" rel="noreferrer noopener">Distro pusulası</a> dört tane soru ile sizin kullanımınıza uygun olabilecek GNU/Linux dağıtımları öneriyor.  Algoritması şu an yeterli olsa da daha ne kadar geliştirebilirim diye üzerinde çalışacağım. Üzerindeki çalışmam bittiğinde de açık kaynak olarak yayınlamayı düşünüyorum.</p>



<p class="wp-block-paragraph">Şimdilik bu kadar yazdığım yeter. Zaten yeni tema ile birlikte yazı sürümleri de güncelleme yapılan tarih ile birlikte gösteriliyor. Eğer tamamlayabilirsem şu an bu yazıyı okuduğun temayı da açık kaynak olarak yayınlayacağım.</p>
<p><a href="https://alikarahisar.com/blog/gule-gule-windows-hos-geldin-cachyos/">Hoş geldin CachyOS, Güle güle Windows</a> yazısı ilk önce <a href="https://alikarahisar.com/blog">Ali Karahisar</a> üzerinde ortaya çıktı.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://alikarahisar.com/blog/gule-gule-windows-hos-geldin-cachyos/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Spring Cloud vs. Kubernetes: Service Discovery ve Trafik Yönetimi Kime Emanet Edilmeli?</title>
		<link>https://alikarahisar.com/blog/spring-cloud-vs-kubernetes-service-discovery-trafik-yonetimi/</link>
					<comments>https://alikarahisar.com/blog/spring-cloud-vs-kubernetes-service-discovery-trafik-yonetimi/#respond</comments>
		
		<dc:creator><![CDATA[Ali Karahisar]]></dc:creator>
		<pubDate>Fri, 11 Sep 2026 08:59:21 +0000</pubDate>
				<category><![CDATA[DevOps]]></category>
		<category><![CDATA[Mikroservices]]></category>
		<category><![CDATA[Yazılım]]></category>
		<category><![CDATA[Yazılım Mimarisi]]></category>
		<category><![CDATA[Consul vs Kubernetes]]></category>
		<category><![CDATA[hizmet keşfi]]></category>
		<category><![CDATA[Istio trafik yönetimi]]></category>
		<category><![CDATA[Kubernetes ConfigMap]]></category>
		<category><![CDATA[Kubernetes DNS]]></category>
		<category><![CDATA[Kubernetes service discovery]]></category>
		<category><![CDATA[mikroservis yük dengeleme]]></category>
		<category><![CDATA[service discovery]]></category>
		<category><![CDATA[Spring Cloud Eureka]]></category>
		<category><![CDATA[Spring Cloud LoadBalancer]]></category>
		<guid isPermaLink="false">https://alikarahisar.com/blog/?p=58</guid>

					<description><![CDATA[<p>Mikroservis mimarisinde “Spring Cloud mu Kubernetes mi?” sorusu sıkça gündeme gelir. Ancak bir sistem mimarı açısından asıl karar, kullanılacak ürünlerden önce sorumluluk sınırlarıyla ilgilidir: Hizmet keşfi, yük dengeleme ve konfigürasyon yönetiminin hangi bölümü uygulamaya, hangi bölümü platforma ait olmalıdır? Spring Cloud, Eureka ve Consul gibi çözümler bu sorumlulukları uygulama seviyesine yaklaştırır. Kubernetes DNS ve Kubernetes [&#8230;]</p>
<p><a href="https://alikarahisar.com/blog/spring-cloud-vs-kubernetes-service-discovery-trafik-yonetimi/">Spring Cloud vs. Kubernetes: Service Discovery ve Trafik Yönetimi Kime Emanet Edilmeli?</a> yazısı ilk önce <a href="https://alikarahisar.com/blog">Ali Karahisar</a> üzerinde ortaya çıktı.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Mikroservis mimarisinde “Spring Cloud mu Kubernetes mi?” sorusu sıkça gündeme gelir. Ancak bir sistem mimarı açısından asıl karar, kullanılacak ürünlerden önce sorumluluk sınırlarıyla ilgilidir:</p>



<span id="more-58"></span>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">Hizmet keşfi, yük dengeleme ve konfigürasyon yönetiminin hangi bölümü uygulamaya, hangi bölümü platforma ait olmalıdır?</p>
</blockquote>



<p class="wp-block-paragraph">Spring Cloud, Eureka ve Consul gibi çözümler bu sorumlulukları uygulama seviyesine yaklaştırır. Kubernetes DNS ve Kubernetes Service temel ağ yeteneklerini altyapı seviyesinde sunar. Istio gibi Service Mesh ürünleri ise merkezi trafik politikaları, gözlemlenebilirlik ve servisler arası güvenlik katmanı ekler.</p>



<p class="wp-block-paragraph">Doğru seçim yalnızca servis sayısına bağlı değildir. Ekip sayısı, kullanılan programlama dilleri, dağıtım ortamı, güvenlik gereksinimleri ve organizasyonun platform işletme kapasitesi birlikte değerlendirilmelidir.</p>



<h2 class="wp-block-heading">Kısa cevap: Hangi sorumluluk kime verilmeli?</h2>



<p class="wp-block-paragraph">Kubernetes üzerinde çalışan yeni sistemler için genel mimari öneri şöyledir:</p>



<ul class="wp-block-list">
<li>Hizmet keşfi: Kubernetes Service ve DNS</li>



<li>Temel yük dengeleme: Kubernetes ağ katmanı</li>



<li>Gelişmiş L7 trafik politikaları: İhtiyaç varsa Istio</li>



<li>İş anlamı taşıyan retry, fallback ve idempotency: Uygulama</li>



<li>Dağıtım konfigürasyonu: ConfigMap ve GitOps</li>



<li>Dinamik uygulama konfigürasyonu: Gerekiyorsa Spring Cloud Config veya Consul</li>



<li>Hassas bilgiler: KMS veya harici secret manager</li>
</ul>



<p class="wp-block-paragraph">Kubernetes dışında çalışan veya VM, container ve farklı veri merkezlerini birlikte kullanan hibrit sistemlerde Consul güçlü bir alternatiftir. Tamamen Java tabanlı daha küçük yapılarda Eureka da yeterli olabilir.</p>



<p class="wp-block-paragraph">Aynı sorumluluğu hem Spring Cloud hem Kubernetes tarafında kalıcı olarak çözmek ise genellikle gereksiz karmaşıklık yaratır.</p>



<h2 class="wp-block-heading">Service Discovery nedir?</h2>



<p class="wp-block-paragraph">Service discovery, bir servisin iletişim kuracağı diğer servislerin güncel ağ adreslerini bulmasını sağlar.</p>



<p class="wp-block-paragraph">Mikroservis instance’ları ölçekleme, yeniden başlatma veya hata nedeniyle sürekli değişebilir. Bu nedenle uygulamaların sabit IP adreslerine bağlanması sürdürülebilir değildir. Bir servis kataloğu veya kararlı platform adresi gerekir.</p>



<h3 class="wp-block-heading">Spring Cloud Eureka ile hizmet keşfi</h3>



<p class="wp-block-paragraph">Eureka modelinde uygulamalar açılırken kendilerini merkezi registry’ye kaydeder ve düzenli heartbeat gönderir. İstemciler, çağırmak istedikleri servisin instance listesini Eureka’dan alır.</p>



<p class="wp-block-paragraph">Güncel Spring Cloud mimarisinde Eureka hizmet keşfini sağlarken istemci tarafı yük dengeleme Spring Cloud LoadBalancer tarafından gerçekleştirilir. Varsayılan seçim round-robin’dir; farklı seçim stratejileri de uygulanabilir. <a href="https://docs.spring.io/spring-cloud-netflix/reference/spring-cloud-netflix.html">Spring Cloud Netflix</a>, <a href="https://docs.spring.io/spring-cloud-commons/reference/spring-cloud-commons/loadbalancer.html">Spring Cloud LoadBalancer</a></p>



<p class="wp-block-paragraph">Bu yaklaşım özellikle şu koşullarda anlamlıdır:</p>



<ul class="wp-block-list">
<li>Sistem ağırlıklı olarak Spring Boot kullanıyorsa</li>



<li>Servisler VM veya bare metal üzerinde çalışıyorsa</li>



<li>Kubernetes’e geçiş henüz tamamlanmadıysa</li>



<li>Uygulama seviyesinde instance metadata’sına göre seçim gerekiyorsa</li>
</ul>



<p class="wp-block-paragraph">Eureka’nın temel dezavantajı, discovery bilgisini uygulama çalışma zamanının bir parçası hâline getirmesidir. Her servis registry istemcisi taşır; kütüphane sürümleri ve cache davranışları sistemin ağ davranışını etkiler.</p>



<h3 class="wp-block-heading">Consul ile hizmet keşfi</h3>



<p class="wp-block-paragraph">Consul; servis kataloğu, sağlık kontrolleri, HTTP API ve DNS tabanlı keşif sunar. Spring Cloud Consul kullanan uygulamalar HTTP API üzerinden kayıt olabilirken Java dışındaki uygulamalar aynı kataloğa DNS aracılığıyla erişebilir. <a href="https://docs.spring.io/spring-cloud-consul/reference/discovery.html">Spring Cloud Consul</a></p>



<p class="wp-block-paragraph">Consul’un Eureka’ya göre en önemli avantajı platform ve dil bağımsızlığıdır. VM, container, Kubernetes ve farklı veri merkezlerinin birlikte bulunduğu yapılarda ortak bir servis kataloğu oluşturabilir.</p>



<p class="wp-block-paragraph">Bunun karşılığında Consul cluster’ının yüksek erişilebilir biçimde kurulması, izlenmesi, yükseltilmesi ve güvenliğinin sağlanması gerekir.</p>



<h3 class="wp-block-heading">Kubernetes Service Discovery</h3>



<p class="wp-block-paragraph">Kubernetes’te Pod IP adresleri geçicidir. Uygulamaların bağlandığı kararlı kimlik <code>Service</code> nesnesidir.</p>



<p class="wp-block-paragraph">CoreDNS, Kubernetes servisleri için DNS kayıtları oluşturur. Kubernetes de bir servise bağlı güncel Pod listesini EndpointSlice nesneleri üzerinden takip eder. Böylece uygulama <code>http://orders</code> gibi kararlı bir servis adına bağlanabilir. Registry SDK’sı, heartbeat veya istemci tarafında instance listesi yönetmesi gerekmez. <a href="https://kubernetes.io/docs/concepts/services-networking/service/">Kubernetes Service</a>, <a href="https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/">Kubernetes DNS</a></p>



<p class="wp-block-paragraph">Bu modelin başlıca avantajları şunlardır:</p>



<ul class="wp-block-list">
<li>Programlama dilinden bağımsızdır.</li>



<li>Uygulamayı discovery kodundan arındırır.</li>



<li>Pod yaşam döngüsü ile endpoint yönetimini aynı kontrol düzlemine bağlar.</li>



<li>Platform genelinde standart servis adresleme modeli oluşturur.</li>



<li>Kubernetes kullanılıyorsa ek registry işletme ihtiyacını ortadan kaldırır.</li>
</ul>



<p class="wp-block-paragraph">Bu nedenle Kubernetes üzerinde çalışan yeni bir sistemde yalnızca hizmet keşfi için Eureka kurmak çoğunlukla gereksizdir.</p>



<h2 class="wp-block-heading">Spring Cloud LoadBalancer ve Kubernetes Service farkı</h2>



<p class="wp-block-paragraph">Spring Cloud LoadBalancer ile Kubernetes Service aynı sorunu farklı katmanlarda çözer.</p>



<h3 class="wp-block-heading">İstemci tarafı yük dengeleme</h3>



<p class="wp-block-paragraph">Spring Cloud LoadBalancer modelinde uygulama:</p>



<ol start="1" class="wp-block-list">
<li>Discovery sisteminden instance listesini alır.</li>



<li>Uygun instance’ı seçer.</li>



<li>İsteği doğrudan o instance’a gönderir.</li>
</ol>



<p class="wp-block-paragraph">Bu model, istemcinin metadata, bölge veya özel iş bağlamına göre hedef seçmesine olanak tanır. Buna karşılık seçim mantığı her uygulamanın içine dağılır.</p>



<h3 class="wp-block-heading">Altyapı tarafı yük dengeleme</h3>



<p class="wp-block-paragraph">Kubernetes Service modelinde uygulama tek bir servis adresine bağlanır. Trafiğin uygun Pod’a aktarılması kube-proxy veya kullanılan ağ altyapısı tarafından gerçekleştirilir.</p>



<p class="wp-block-paragraph">Bu yaklaşım uygulamayı sadeleştirir ve farklı dillerde yazılmış servisler arasında standart davranış sağlar. Ancak temel Kubernetes Service; HTTP header’ına göre yönlendirme, yüzdesel canary veya request-level circuit breaking gibi gelişmiş L7 politikaları sunmaz.</p>



<p class="wp-block-paragraph">Ayrıca uzun süre açık kalan HTTP/2 veya gRPC bağlantıları dikkatle değerlendirilmelidir. DNS ya da endpoint kümesi değişse bile connection pool aynı backend’e trafik göndermeyi sürdürebilir.</p>



<h2 class="wp-block-heading">Istio trafik yönetimine ne ekler?</h2>



<p class="wp-block-paragraph">Istio, Kubernetes service discovery mekanizmasının yerine geçmez. Kubernetes servis ve endpoint bilgisini kullanır; bunun üzerine programlanabilir bir trafik katmanı ekler.</p>



<p class="wp-block-paragraph">Istio ile merkezi olarak şu politikalar uygulanabilir:</p>



<ul class="wp-block-list">
<li>Ağırlıklı ve sürüm bazlı yönlendirme</li>



<li>Canary ve aşamalı dağıtım</li>



<li>HTTP header veya path bazlı routing</li>



<li>Timeout ve retry</li>



<li>Circuit breaking</li>



<li>Sağlıksız instance’ları havuzdan çıkarma</li>



<li>Trafik yansıtma</li>



<li>Servisler arası mTLS</li>



<li>Kimlik tabanlı erişim politikaları</li>



<li>Ortak trafik metriği ve erişim kayıtları</li>
</ul>



<p class="wp-block-paragraph">Istio’nun trafik modeli Envoy proxy’leri kullanır. Sidecar modelinde her Pod’un yanında bir proxy çalışır. Ambient modelde temel L4 işlevleri node seviyesinde sağlanabilir; L7 özellikleri gerektiğinde waypoint proxy devreye girer. <a href="https://istio.io/latest/docs/concepts/traffic-management/">Istio Traffic Management</a>, <a href="https://istio.io/latest/docs/overview/dataplane-modes/">Istio veri düzlemi modelleri</a></p>



<h3 class="wp-block-heading">Istio’nun avantajları</h3>



<ul class="wp-block-list">
<li>Farklı dillerdeki servisler için ortak ağ politikası sağlar.</li>



<li>Trafik davranışı uygulama sürümünden bağımsız değiştirilebilir.</li>



<li>Canary ve progressive delivery süreçlerini kolaylaştırır.</li>



<li>mTLS ve servis kimliğini merkezi hâle getirir.</li>



<li>Gözlemlenebilirlik standardı oluşturur.</li>



<li>Politika yönetimini platform ekibinde toplar.</li>
</ul>



<h3 class="wp-block-heading">Istio’nun maliyetleri</h3>



<ul class="wp-block-list">
<li>Yeni bir kontrol ve veri düzlemi işletilir.</li>



<li>Proxy’ler CPU, bellek ve belirli ölçüde gecikme ekler.</li>



<li>Hata ayıklama süreci daha karmaşık hâle gelir.</li>



<li>Yanlış bir merkezi politika çok sayıda servisi etkileyebilir.</li>



<li>Proxy ve kontrol düzlemi yükseltmeleri dikkatle yönetilmelidir.</li>



<li>Platform ekibinde Service Mesh uzmanlığı gerekir.</li>
</ul>



<p class="wp-block-paragraph">Dolayısıyla Istio, “mikroservis kullanıyoruz” gerekçesiyle kurulacak bir bileşen değildir. Sağladığı merkezi politika değeri, işletme maliyetini geçtiğinde kullanılmalıdır.</p>



<h2 class="wp-block-heading">Konfigürasyon yönetimi: Spring Cloud Config mi ConfigMap mi?</h2>



<p class="wp-block-paragraph">Konfigürasyon yönetimi, service discovery ve trafik yönetiminden ayrı ele alınmalıdır. Istio uygulamanın iş konfigürasyonunu yönetmez.</p>



<h3 class="wp-block-heading">Spring Cloud Config</h3>



<p class="wp-block-paragraph">Spring Cloud Config, uygulama ayarlarını merkezi bir servis üzerinden sunar. Git deposu kullanıldığında değişiklik geçmişi, sürümleme ve ortam bazlı konfigürasyon yönetimi elde edilir.</p>



<p class="wp-block-paragraph">Şu durumlarda güçlüdür:</p>



<ul class="wp-block-list">
<li>Uygulamalar Kubernetes dışında da çalışıyorsa</li>



<li>Spring profile modeli yoğun kullanılıyorsa</li>



<li>Uygulama ve ortam bazlı konfigürasyon hiyerarşisi gerekiyorsa</li>



<li>Konfigürasyonun deployment’tan bağımsız yenilenmesi isteniyorsa</li>
</ul>



<p class="wp-block-paragraph">Dezavantajı, Config Server’ın uygulama başlangıcında ek bir bağımlılığa dönüşmesidir. Dinamik refresh sırasında bütün bean’lerin ve bağlantı havuzlarının yeni değerlerle güvenli biçimde çalışacağı da garanti değildir.</p>



<h3 class="wp-block-heading">Consul Config</h3>



<p class="wp-block-paragraph">Spring Cloud Consul Config, Consul KV üzerinden uygulama ayarlarını yükleyebilir. Config Watch, ilgili değerlerde değişiklik algılandığında Spring refresh olayı oluşturabilir. <a href="https://docs.spring.io/spring-cloud-consul/reference/config.html">Spring Cloud Consul Config</a></p>



<p class="wp-block-paragraph">Discovery için zaten Consul kullanılan hibrit sistemlerde operasyonel açıdan çekici olabilir. Ancak aynı Consul cluster’ının hem discovery hem konfigürasyon için kullanılması hata alanını büyütür. Consul kesintisinin iki kritik fonksiyonu birden etkilemesi hesaba katılmalıdır.</p>



<h3 class="wp-block-heading">Kubernetes ConfigMap</h3>



<p class="wp-block-paragraph">Kubernetes ConfigMap, hassas olmayan uygulama ayarlarını container imajından ayırır. Değerler ortam değişkeni veya volume içindeki dosyalar olarak Pod’a aktarılabilir.</p>



<p class="wp-block-paragraph">Volume olarak bağlanan ConfigMap güncellemeleri zaman içinde Pod’a yansıtılabilir; fakat uygulamanın dosyayı yeniden okuması gerekir. Ortam değişkeni olarak verilen ConfigMap değerleri otomatik güncellenmez ve Pod yeniden başlatılmalıdır. <a href="https://kubernetes.io/docs/concepts/configuration/configmap/">Kubernetes ConfigMap</a></p>



<p class="wp-block-paragraph">Birçok üretim sistemi için kontrollü rolling restart, dinamik context refresh’ten daha güvenlidir. Yeni konfigürasyon yeni Pod’larda doğrulanır; readiness kontrolü başarısız olursa rollout durdurulabilir.</p>



<h3 class="wp-block-heading">Kubernetes Secret yeterli mi?</h3>



<p class="wp-block-paragraph">Kubernetes Secret nesnesi kullanmak tek başına güvenli secret yönetimi anlamına gelmez. Secret verileri varsayılan olarak etcd içinde şifrelenmeden saklanabilir.</p>



<p class="wp-block-paragraph">Üretim ortamında şu kontroller gerekir:</p>



<ul class="wp-block-list">
<li>Encryption-at-rest</li>



<li>Sıkı RBAC</li>



<li>Secret erişim kayıtları</li>



<li>Düzenli rotasyon</li>



<li>Tercihen KMS veya harici secret manager entegrasyonu</li>
</ul>



<p class="wp-block-paragraph"><a href="https://kubernetes.io/docs/concepts/security/secrets-good-practices/">Kubernetes Secret güvenlik önerileri</a></p>



<h2 class="wp-block-heading">Retry ve circuit breaker uygulamada mı mesh’te mi olmalı?</h2>



<p class="wp-block-paragraph">Bu sorumluluk bütünüyle tek bir katmana verilemez.</p>



<p class="wp-block-paragraph">Mesh şu teknik kararları üstlenebilir:</p>



<ul class="wp-block-list">
<li>Bağlantı kurulamazsa farklı endpoint denemek</li>



<li>Belirli hata kodlarında sınırlı retry yapmak</li>



<li>Sağlıksız instance’ı geçici olarak havuzdan çıkarmak</li>



<li>Ağ seviyesinde timeout standardı uygulamak</li>
</ul>



<p class="wp-block-paragraph">Uygulamada kalması gereken kararlar ise şunlardır:</p>



<ul class="wp-block-list">
<li>Bir ödeme isteği güvenle tekrar edilebilir mi?</li>



<li>İşlem idempotent mi?</li>



<li>Hangi fallback iş açısından kabul edilebilir?</li>



<li>Kısmi başarı nasıl yönetilmeli?</li>



<li>Telafi işlemi gerekiyor mu?</li>
</ul>



<p class="wp-block-paragraph">Mesh, bir HTTP isteğinin teknik olarak tekrar edilebilir göründüğünü anlayabilir; fakat tekrarın ikinci kez tahsilat yaratıp yaratmayacağını bilemez.</p>



<p class="wp-block-paragraph">Ayrıca uygulama ve mesh bağımsız retry yaptığında deneme sayıları çarpılır. Uygulamadaki üç deneme ile mesh’teki üç deneme, tek kullanıcı isteğini dokuz downstream çağrısına dönüştürebilir. Retry bütçesi uçtan uca tasarlanmalıdır.</p>



<h2 class="wp-block-heading">Proje ölçeğine göre mimari tercih</h2>



<p class="wp-block-paragraph">Servis sayıları kesin sınır değildir. Ekip sayısı, teknoloji çeşitliliği, güvenlik gereksinimi ve dağıtım sıklığı daha belirleyicidir.</p>



<h3 class="wp-block-heading">Küçük projeler: 1–2 ekip ve yaklaşık 3–10 servis</h3>



<p class="wp-block-paragraph">Kubernetes üzerinde çalışılıyorsa:</p>



<ul class="wp-block-list">
<li>Kubernetes DNS ve Service kullanın.</li>



<li>ConfigMap ve kontrollü rolling deployment tercih edin.</li>



<li>Istio kurmayın.</li>



<li>Timeout, idempotency ve iş fallback’lerini uygulamada yönetin.</li>
</ul>



<p class="wp-block-paragraph">Bu ölçekte Service Mesh’in işletme ve öğrenme maliyeti genellikle faydasından büyüktür.</p>



<p class="wp-block-paragraph">VM veya karma altyapıda çalışılıyorsa:</p>



<ul class="wp-block-list">
<li>Tamamen Java tabanlı sistemlerde Eureka kullanılabilir.</li>



<li>Birden fazla dil veya çalışma ortamı varsa Consul tercih edilebilir.</li>



<li>Discovery ihtiyacı tek başına Kubernetes’e geçiş gerekçesi olmamalıdır.</li>
</ul>



<h3 class="wp-block-heading">Orta ölçekli projeler: 3–8 ekip ve yaklaşık 10–50 servis</h3>



<p class="wp-block-paragraph">Kubernetes’i varsayılan discovery ve temel load-balancing katmanı yapın. Yeni servislerin ayrıca Eureka’ya kaydolmasını istemeyin.</p>



<p class="wp-block-paragraph">Istio’yu şu ihtiyaçlar somutlaştığında değerlendirin:</p>



<ul class="wp-block-list">
<li>Takımlar arasındaki retry ve timeout farklılıkları üretim olaylarına yol açıyorsa</li>



<li>Merkezi canary dağıtımı gerekiyorsa</li>



<li>Servisler arası mTLS zorunluysa</li>



<li>Polyglot yapı ortak ağ telemetrisi gerektiriyorsa</li>



<li>Merkezi erişim politikaları yönetilecekse</li>
</ul>



<p class="wp-block-paragraph">Mesh’e geçiş kritik servis veya namespace’lerle kademeli olarak başlatılabilir.</p>



<h3 class="wp-block-heading">Büyük projeler: 8’den fazla ekip, 50+ servis veya çoklu cluster</h3>



<p class="wp-block-paragraph">Bu ölçekte her uygulama ekibinin aynı ağ politikalarını farklı kütüphanelerle uygulaması sürdürülemez hâle gelebilir.</p>



<p class="wp-block-paragraph">Önerilen sorumluluk dağılımı:</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><tbody><tr><th>Sorumluluk</th><th>Önerilen sahip</th></tr><tr><td>Hizmet keşfi</td><td>Kubernetes Service ve DNS</td></tr><tr><td>Endpoint yaşam döngüsü</td><td>Kubernetes readiness ve EndpointSlice</td></tr><tr><td>Temel yük dengeleme</td><td>Kubernetes ağ katmanı</td></tr><tr><td>Gelişmiş trafik yönetimi</td><td>Istio</td></tr><tr><td>Servis kimliği ve mTLS</td><td>Istio</td></tr><tr><td>İş seviyesinde retry ve fallback</td><td>Uygulama</td></tr><tr><td>Dağıtım konfigürasyonu</td><td>GitOps ve ConfigMap</td></tr><tr><td>Hassas bilgiler</td><td>KMS veya harici secret manager</td></tr><tr><td>Platform standartları</td><td>Platform ekibi</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">Mesh’in anlamlı olmasını sağlayan yalnızca servis sayısı değildir. Merkezi güvenlik, çoklu dil desteği, yüksek dağıtım sıklığı ve çapraz takım standardizasyonu operasyon maliyetini karşılayacak düzeye ulaşmalıdır.</p>



<h2 class="wp-block-heading">Kaçınılması gereken yaklaşım: Çift sahiplik</h2>



<p class="wp-block-paragraph">Geçiş dönemi dışında şu kombinasyonlardan kaçınılmalıdır:</p>



<ul class="wp-block-list">
<li>Eureka ve Kubernetes’in paralel servis kataloğu olması</li>



<li>Spring Cloud LoadBalancer ile Istio’nun bağımsız hedef seçmesi</li>



<li>Uygulama, API gateway ve mesh’in ayrı ayrı retry uygulaması</li>



<li>Timeout değerlerinin farklı katmanlarda çelişmesi</li>



<li>Aynı ayarın Consul KV ve ConfigMap’te tutulması</li>
</ul>



<p class="wp-block-paragraph">Her teknik sorumluluk için tek bir birincil doğruluk kaynağı ve açık bir sahip belirlenmelidir.</p>



<h2 class="wp-block-heading">Sonuç: Spring Cloud mu Kubernetes mi?</h2>



<p class="wp-block-paragraph">Spring Cloud ve Kubernetes birbirlerinin tam alternatifi değildir.</p>



<p class="wp-block-paragraph">Spring Cloud uygulamaya discovery ve istemci davranışı kazandırır. Kubernetes temel platform bağlantısını ve servis yaşam döngüsünü yönetir. Istio ise ortak trafik, güvenlik ve gözlemlenebilirlik politikalarını uygulamadan ayırır.</p>



<p class="wp-block-paragraph">Kubernetes kullanan yeni sistemlerde service discovery ve temel load balancing platforma bırakılmalıdır. Istio yalnızca servis sayısı arttığı için değil, ortak trafik ve güvenlik politikalarının uygulama ekipleri tarafından ayrı ayrı yönetilmesi sürdürülemez hâle geldiğinde eklenmelidir.</p>



<p class="wp-block-paragraph">Kubernetes dışında veya ciddi hibrit altyapıda Consul güçlü bir seçimdir. Tamamen Java ve VM tabanlı küçük sistemlerde Eureka hâlâ yeterli olabilir. Kubernetes’e yerleşmiş bir platformda Eureka’yı kalıcı biçimde tutmak ise çoğunlukla aynı gerçeği iki kontrol düzleminde yönetmek anlamına gelir.</p>



<p class="wp-block-paragraph">Sağlıklı sorumluluk ayrımı şöyledir:</p>



<ul class="wp-block-list">
<li>Platform, servisin nerede olduğunu bilir.</li>



<li>Mesh, trafiğin teknik olarak nasıl akacağını bilir.</li>



<li>Uygulama, isteğin iş açısından ne anlama geldiğini bilir.</li>



<li>Konfigürasyon sistemi, uygulamanın hangi değerlerle çalışacağını bilir.</li>
</ul>



<p class="wp-block-paragraph">İyi mimari, bu sınırların açık olduğu ve aynı kararın birden fazla katmanda tekrar verilmediği mimaridir.</p>



<h2 class="wp-block-heading">Sık sorulan sorular</h2>



<h3 class="wp-block-heading">Kubernetes kullanırken Eureka gerekli mi?</h3>



<p class="wp-block-paragraph">Çoğu yeni Kubernetes sistemi için hayır. Kubernetes Service, DNS ve EndpointSlice temel hizmet keşfi ihtiyacını karşılar. Eureka ancak Kubernetes dışındaki servislerle geçiş, özel metadata tabanlı seçim veya güçlü bir mevcut bağımlılık varsa geçici olarak anlamlı olabilir.</p>



<h3 class="wp-block-heading">Kubernetes Service, Istio’nun yerini tutar mı?</h3>



<p class="wp-block-paragraph">Hayır. Kubernetes Service temel servis adresleme ve L4 yönlendirme sağlar. Istio ise canary, header tabanlı routing, mTLS, retry, circuit breaking ve ayrıntılı trafik gözlemlenebilirliği gibi gelişmiş politikalar ekler.</p>



<h3 class="wp-block-heading">Küçük bir mikroservis projesinde Istio kullanılmalı mı?</h3>



<p class="wp-block-paragraph">Genellikle hayır. Birkaç servis ve tek ekipten oluşan sistemlerde operasyonel maliyet çoğu zaman kazanımdan yüksektir. Önce Kubernetes’in yerleşik özellikleri ve doğru uygulama timeout’ları kullanılmalıdır.</p>



<h3 class="wp-block-heading">Consul mu Eureka mı daha uygun?</h3>



<p class="wp-block-paragraph">Tamamen Spring Boot kullanan sınırlı bir sistemde Eureka daha basit olabilir. Birden fazla dil, VM, container, veri merkezi veya hibrit altyapı söz konusuysa Consul daha geniş bir entegrasyon alanı sunar.</p>



<h3 class="wp-block-heading">Konfigürasyon değişikliği için dinamik refresh mi rolling restart mı?</h3>



<p class="wp-block-paragraph">Çoğu üretim konfigürasyonu için rolling restart daha öngörülebilir ve geri alınabilir bir modeldir. Feature flag gibi anlık değişmesi gereken değerlerde kontrollü dinamik konfigürasyon tercih edilebilir.</p>



<p class="wp-block-paragraph"></p>
<p><a href="https://alikarahisar.com/blog/spring-cloud-vs-kubernetes-service-discovery-trafik-yonetimi/">Spring Cloud vs. Kubernetes: Service Discovery ve Trafik Yönetimi Kime Emanet Edilmeli?</a> yazısı ilk önce <a href="https://alikarahisar.com/blog">Ali Karahisar</a> üzerinde ortaya çıktı.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://alikarahisar.com/blog/spring-cloud-vs-kubernetes-service-discovery-trafik-yonetimi/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>GraalVM Native Image ile Serverless ve Mikroservislerde Cold Start Sorununu Çözmek</title>
		<link>https://alikarahisar.com/blog/graalvm-native-image-cold-start-spring-boot-3/</link>
					<comments>https://alikarahisar.com/blog/graalvm-native-image-cold-start-spring-boot-3/#respond</comments>
		
		<dc:creator><![CDATA[Ali Karahisar]]></dc:creator>
		<pubDate>Thu, 03 Sep 2026 11:16:22 +0000</pubDate>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Yazılım]]></category>
		<category><![CDATA[AOT derleme]]></category>
		<category><![CDATA[cloud-native Java]]></category>
		<category><![CDATA[GraalVM Native Image]]></category>
		<category><![CDATA[Java cold start]]></category>
		<category><![CDATA[JIT derleme]]></category>
		<category><![CDATA[memory footprint]]></category>
		<category><![CDATA[mikroservis performansı]]></category>
		<category><![CDATA[native executable]]></category>
		<category><![CDATA[serverless Java]]></category>
		<category><![CDATA[Spring AOT]]></category>
		<category><![CDATA[Spring Boot 3 native image]]></category>
		<guid isPermaLink="false">https://alikarahisar.com/blog/?p=44</guid>

					<description><![CDATA[<p>Java; güçlü ekosistemi, olgun araçları ve yüksek çalışma zamanı performansıyla mikroservis geliştirmede en güvenilir seçeneklerden biri. Ancak konu serverless fonksiyonlar, kısa ömürlü container’lar ve hızla ölçeklenen Kubernetes iş yükleri olduğunda klasik JVM modelinin iki maliyeti daha görünür hâle gelir: başlangıç süresi ve bellek tüketimi. Yeni bir instance’ın JVM’i başlatması, sınıfları yüklemesi, framework context’ini oluşturması ve [&#8230;]</p>
<p><a href="https://alikarahisar.com/blog/graalvm-native-image-cold-start-spring-boot-3/">GraalVM Native Image ile Serverless ve Mikroservislerde Cold Start Sorununu Çözmek</a> yazısı ilk önce <a href="https://alikarahisar.com/blog">Ali Karahisar</a> üzerinde ortaya çıktı.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Java; güçlü ekosistemi, olgun araçları ve yüksek çalışma zamanı performansıyla mikroservis geliştirmede en güvenilir seçeneklerden biri. Ancak konu serverless fonksiyonlar, kısa ömürlü container’lar ve hızla ölçeklenen Kubernetes iş yükleri olduğunda klasik JVM modelinin iki maliyeti daha görünür hâle gelir: başlangıç süresi ve bellek tüketimi.</p>



<span id="more-44"></span>



<p class="wp-block-paragraph">Yeni bir instance’ın JVM’i başlatması, sınıfları yüklemesi, framework context’ini oluşturması ve kodu optimize etmesi zaman alır. İstek trafiğinin olmadığı bir sürenin ardından fonksiyon yeniden oluşturuluyorsa kullanıcı bu maliyeti doğrudan “cold start” gecikmesi olarak hisseder.</p>



<p class="wp-block-paragraph">GraalVM Native Image, Java uygulamasını dağıtım öncesinde makine koduna derleyerek bu denklemi değiştirir. Uygulama, çalışma anında JAR ve tam bir JVM başlatmak yerine işletim sistemi üzerinde doğrudan çalışan platforma özgü bir executable olarak açılır. GraalVM belgelerine göre native executable yalnızca erişilebilir uygulama ve JDK kodunu, gerekli çalışma zamanı bileşenlerini ve statik olarak bağlanan yerel kodu içerir; bunun sonucu milisaniyeler düzeyinde başlangıç, warm-up gerektirmeyen performans ve daha düşük kaynak tüketimidir. (<a href="https://www.graalvm.org/latest/reference-manual/native-image/">GraalVM Native Image belgeleri</a>)</p>



<h2 class="wp-block-heading">Cold start neden oluşur?</h2>



<p class="wp-block-paragraph">Bir Java mikroservisinin ilk isteğe hazır hâle gelmeden önce tipik olarak şu işlemleri tamamlaması gerekir:</p>



<ol start="1" class="wp-block-list">
<li>JVM süreci başlatılır.</li>



<li>Uygulama sınıfları ve bağımlılıklar yüklenir.</li>



<li>Bytecode doğrulanır ve yorumlanmaya başlanır.</li>



<li>Spring ApplicationContext oluşturulur.</li>



<li>Bean tanımları, koşullu konfigürasyonlar ve proxy’ler hazırlanır.</li>



<li>Sık çalışan kod yolları belirlenir ve JIT compiler tarafından optimize edilir.</li>



<li>Bağlantı havuzları ve harici servis istemcileri hazırlanır.</li>
</ol>



<p class="wp-block-paragraph">Uzun süre çalışan bir serviste bu maliyet yalnızca bir kez ödenir ve JIT optimizasyonları zaman içinde önemli throughput avantajı sağlar. Buna karşılık serverless platformlarda, scale-to-zero kullanan Kubernetes ortamlarında veya çok sık pod oluşturan sistemlerde başlangıç maliyeti tekrar tekrar ödenebilir.</p>



<p class="wp-block-paragraph">Cold start yalnızca uygulamanın <code>main</code> metoduna ulaşma süresi değildir. Gerçek ölçüm, yeni instance’ın ilk isteğe doğru yanıt verebildiği ana kadar geçen süreyi kapsamalıdır.</p>



<h2 class="wp-block-heading">JIT ve AOT arasındaki mimari fark</h2>



<h3 class="wp-block-heading">JIT: Çalışırken öğrenen optimizasyon</h3>



<p class="wp-block-paragraph">Geleneksel JVM modelinde Java kaynak kodu önce bytecode’a çevrilir:</p>



<pre class="wp-block-code"><code>Java kaynak kodu
        ↓
     Bytecode
        ↓
 JVM + Interpreter
        ↓
   JIT Compiler
        ↓
Optimize makine kodu</code></pre>



<p class="wp-block-paragraph">JIT, uygulamanın gerçek çalışma davranışını gözlemler. Hangi metotların sık çağrıldığını, dallanmaların hangi yönde ilerlediğini ve hangi kod yollarının “hot” olduğunu belirleyerek bu bölümleri çalışma sırasında optimize eder.</p>



<p class="wp-block-paragraph">Bunun önemli bir avantajı vardır: Optimizasyon kararları gerçek trafik profilinden beslenir. Uzun süre çalışan ve CPU yoğun işlem yapan servislerde JVM, yeterli warm-up sonrasında çok yüksek tepe performansına ulaşabilir.</p>



<p class="wp-block-paragraph">Bedeli ise JVM’in, derleyicinin, profil verilerinin ve sınıf metadata’sının bellekte bulunmasıdır. Uygulama ayrıca en iyi performansına hemen değil, belirli bir ısınma süresinden sonra ulaşır.</p>



<h3 class="wp-block-heading">AOT: Çalışmadan önce tamamlanan derleme</h3>



<p class="wp-block-paragraph">GraalVM Native Image, bytecode’u build aşamasında analiz eder ve platforma özgü makine koduna dönüştürür:</p>



<pre class="wp-block-code"><code>Uygulama + bağımlılıklar + JDK sınıfları
                    ↓
      Erişilebilirlik analizi
                    ↓
       AOT optimizasyon ve derleme
                    ↓
 Kod + image heap + native runtime
                    ↓
        Native executable</code></pre>



<p class="wp-block-paragraph">Analiz <code>main</code> metodu gibi giriş noktalarından başlar. Erişilebilen sınıflar, metotlar ve alanlar tekrarlı biçimde bulunur; kullanılmadığı belirlenen parçalar native executable’a eklenmez. Derlemeden sonra yeni program elemanlarının çalışma anında sisteme eklenememesi “closed-world assumption”, yani kapalı dünya varsayımı olarak adlandırılır. (<a href="https://www.graalvm.org/latest/reference-manual/native-image/basics/">Native Image çalışma modeli</a>)</p>



<p class="wp-block-paragraph">Bazı sınıflar build sırasında initialize edilebilir. Oluşan nesneler ve statik durum “image heap” içerisine kaydedilir. Uygulama açıldığında bu işlerin yeniden yapılmaması startup süresini kısaltır.</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><tbody><tr><th>Özellik</th><th>JVM + JIT</th><th>GraalVM Native Image + AOT</th></tr><tr><td>Derleme zamanı</td><td>Çalışma sırasında devam eder</td><td>Büyük ölçüde build sırasında tamamlanır</td></tr><tr><td>Başlangıç</td><td>Class loading ve warm-up nedeniyle daha yavaş</td><td>Genellikle çok hızlı</td></tr><tr><td>Bellek</td><td>JVM, JIT ve metadata maliyeti vardır</td><td>Genellikle daha düşük RSS</td></tr><tr><td>Tepe throughput</td><td>Uzun ömürlü işlerde çok güçlü</td><td>İş yüküne göre JIT’in gerisinde kalabilir</td></tr><tr><td>Dinamik sınıf yükleme</td><td>Doğal olarak desteklenir</td><td>Kapalı dünya varsayımıyla sınırlıdır</td></tr><tr><td>Build süresi</td><td>Görece kısa</td><td>Daha uzun ve daha fazla kaynak tüketebilir</td></tr><tr><td>Dağıtım</td><td>JAR ve uyumlu JVM gerekir</td><td>İşletim sistemi ve mimariye özel binary gerekir</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">AOT her durumda JIT’ten daha hızlı demek değildir. Native Image’ın temel avantajı başlangıç ve kaynak verimliliğidir. Uzun süre çalışan, CPU yoğun servislerde JIT’in çalışma zamanı profiline dayanan optimizasyonları daha yüksek throughput sağlayabilir. GraalVM’in Profile-Guided Optimization özelliği, temsilî trafikten profil toplayıp bunu AOT derlemeye taşıyarak aradaki farkı azaltabilir. (<a href="https://www.graalvm.org/dev/reference-manual/native-image/optimizations-and-performance/">GraalVM optimizasyon belgeleri</a>)</p>



<h2 class="wp-block-heading">Startup süresi ve memory footprint üzerindeki etkisi</h2>



<p class="wp-block-paragraph">Native executable çalıştırılırken tam bir HotSpot JVM’in başlatılmasına ve uygulama kodunun JIT tarafından ısıtılmasına gerek kalmaz. Gereksiz sınıfların binary’ye alınmaması da class metadata, JIT code cache ve derleyici altyapısının çalışma zamanı maliyetlerini azaltır.</p>



<p class="wp-block-paragraph">Cloud-native sistemlerde bu özellikler üç alanda değer üretir:</p>



<ul class="wp-block-list">
<li>Serverless fonksiyonlar ilk isteğe daha kısa sürede cevap verebilir.</li>



<li>Kubernetes pod’ları daha hızlı hazır duruma gelerek scale-out gecikmesini azaltabilir.</li>



<li>Aynı bellek bütçesi içerisinde daha fazla servis instance’ı çalıştırılabilir.</li>
</ul>



<p class="wp-block-paragraph">Ancak sonucu tek bir evrensel yüzdeyle ifade etmek doğru değildir. Kazanç; kullanılan framework’e, dependency sayısına, heap ayarlarına, GC seçimine, container limitlerine ve uygulamanın başlangıç sırasında yaptığı I/O işlemlerine bağlıdır.</p>



<p class="wp-block-paragraph">Örneğin uygulama açılışta veritabanı migrasyonu çalıştırıyor veya uzak bir secret servisine bağlanıyorsa native executable bu ağ gecikmesini ortadan kaldırmaz. GraalVM kod başlatma maliyetini azaltır; dış sistemlerin gecikmesini sihirli biçimde çözmez.</p>



<p class="wp-block-paragraph">Bu nedenle JVM ve native sürümleri aynı koşullarda karşılaştırın:</p>



<pre class="wp-block-code"><code># JVM sürümü
/usr/bin/time -v java -jar target/cold-start-demo.jar

# Native sürüm
/usr/bin/time -v ./target/cold-start-demo</code></pre>



<p class="wp-block-paragraph">macOS üzerinde ayrıntılı süreç ölçümü için:</p>



<pre class="wp-block-code"><code>/usr/bin/time -l ./target/cold-start-demo</code></pre>



<p class="wp-block-paragraph">Yalnızca konsoldaki “Started” mesajını değil, aşağıdaki metrikleri kaydedin:</p>



<ul class="wp-block-list">
<li>Process başlangıcından readiness durumuna kadar geçen süre</li>



<li>İlk isteğin p50, p95 ve p99 gecikmesi</li>



<li>Warm request gecikmesi</li>



<li>Peak RSS ve steady-state RSS</li>



<li>CPU tüketimi</li>



<li>Binary veya container image boyutu</li>



<li>Native image build süresi</li>



<li>Gerçek trafik altındaki throughput</li>
</ul>



<h2 class="wp-block-heading">GraalVM Native Image’ın zorlandığı alanlar</h2>



<h3 class="wp-block-heading">1. Reflection</h3>



<p class="wp-block-paragraph">Reflection kullanılan kodda çağrılacak sınıf veya metot adı çalışma anında bir konfigürasyondan gelebilir. Statik analiz bu ilişkiyi göremiyorsa ilgili sınıfın kullanılmadığını düşünerek onu binary dışında bırakabilir.</p>



<p class="wp-block-paragraph">Sonuç olarak uygulama başarıyla derlense bile yalnızca belirli bir endpoint çağrıldığında hata verebilir. JSON serileştirme, ORM, dependency injection ve plugin tabanlı kütüphaneler bu açıdan özellikle test edilmelidir.</p>



<p class="wp-block-paragraph">Spring Boot 3’ün AOT motoru controller dönüş tipleri, bean tanımları ve pek çok framework kullanımına ait hint’leri otomatik üretir. Otomatik çıkarımın yetmediği durumlarda <code>@RegisterReflectionForBinding</code> veya <code>RuntimeHintsRegistrar</code> kullanılabilir. (<a href="https://docs.spring.io/spring-boot/reference/packaging/native-image/advanced-topics.html">Spring Boot özel native hint rehberi</a>)</p>



<p class="wp-block-paragraph">Örneğin çalışma anında JSON’a dönüştürülecek bir sınıfı açıkça kaydetmek için:</p>



<pre class="wp-block-code"><code>import org.springframework.aot.hint.annotation.RegisterReflectionForBinding;
import org.springframework.context.annotation.Configuration;

@Configuration
@RegisterReflectionForBinding(PaymentRequest.class)
class NativeConfiguration {
}</code></pre>



<h3 class="wp-block-heading">2. Dynamic proxy</h3>



<p class="wp-block-paragraph">Spring AOP, declarative transaction yönetimi ve bazı HTTP client kütüphaneleri çalışma anında proxy oluşturabilir. JVM üzerinde ihtiyaç anında üretilebilen bu sınıfların native executable içinde önceden bilinmesi gerekir.</p>



<p class="wp-block-paragraph">Spring AOT, kendi tanıyabildiği proxy’leri build sırasında üretir. Uygulamaya özgü bir JDK proxy için runtime hint eklenebilir:</p>



<pre class="wp-block-code"><code>import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;

class ApplicationRuntimeHints implements RuntimeHintsRegistrar {

    @Override
    public void registerHints(
            RuntimeHints hints,
            ClassLoader classLoader) {

        hints.proxies().registerJdkProxy(PaymentClient.class);
    }
}</code></pre>



<p class="wp-block-paragraph">Registrar’ı uygulamaya bağlayalım:</p>



<pre class="wp-block-code"><code>import org.springframework.context.annotation.ImportRuntimeHints;

@SpringBootApplication
@ImportRuntimeHints(ApplicationRuntimeHints.class)
public class PaymentApplication {

    public static void main(String&#91;] args) {
        SpringApplication.run(PaymentApplication.class, args);
    }
}</code></pre>



<p class="wp-block-paragraph">Spring’in CGLIB tabanlı proxy’leri de JVM’de çalışma anında oluşturulurken native build sürecinde önceden üretilir. Oluşturulan proxy sınıfları Maven projelerinde <code>target/spring-aot/main/classes</code> altında incelenebilir. (<a href="https://docs.spring.io/spring-boot/reference/packaging/native-image/introducing-graalvm-native-images.html">Spring AOT proxy üretimi</a>)</p>



<h3 class="wp-block-heading">3. Classpath kaynakları</h3>



<p class="wp-block-paragraph">JSON şablonları, sertifikalar, SQL dosyaları, localization bundle’ları veya statik dosyalar Java kodu olmadığı için yalnızca erişilebilirlik analiziyle her zaman bulunamaz.</p>



<p class="wp-block-paragraph">Bir kaynağı Spring runtime hints API ile eklemek mümkündür:</p>



<pre class="wp-block-code"><code>@Override
public void registerHints(
        RuntimeHints hints,
        ClassLoader classLoader) {

    hints.resources().registerPattern("templates/*.json");
    hints.resources().registerPattern("db/migration/**");
}</code></pre>



<p class="wp-block-paragraph">GraalVM ayrıca <code>META-INF/native-image</code> altındaki <code>reachability-metadata.json</code> dosyalarını okuyabilir:</p>



<pre class="wp-block-code"><code>{
  "resources": &#91;
    {
      "glob": "templates/*.json"
    }
  ]
}</code></pre>



<p class="wp-block-paragraph">Native executable içine gömülen kaynaklar gerçek bir dosya sistemi yolu olmayabilir. Bu nedenle <code>URL#getFile()</code> yerine <code>getResourceAsStream()</code> veya <code>URL#openStream()</code> tercih edilmelidir. Gerekli metadata bulunmayan dinamik erişimler çalışma zamanı hatasına dönüşebilir. (<a href="https://www.graalvm.org/jdk25/reference-manual/native-image/metadata/">GraalVM Reachability Metadata</a>)</p>



<h2 class="wp-block-heading">Spring Boot 3 ile native executable oluşturma</h2>



<p class="wp-block-paragraph">Aşağıdaki örnek Maven tabanlı bir Spring Boot 3 projesini temel alır.</p>



<h3 class="wp-block-heading">Adım 1: Projeyi oluşturun</h3>



<p class="wp-block-paragraph"><a href="https://start.spring.io/">start.spring.io</a> üzerinden şu seçimlerle bir proje oluşturabilirsiniz:</p>



<ul class="wp-block-list">
<li>Java 21 gibi desteklenen bir LTS sürümü</li>



<li>Spring Boot 3.x</li>



<li>Maven</li>



<li>Spring Web</li>



<li>GraalVM Native Support</li>
</ul>



<p class="wp-block-paragraph">Basit bir endpoint ekleyelim:</p>



<pre class="wp-block-code"><code>package com.example.coldstart;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@SpringBootApplication
public class ColdStartApplication {

    @GetMapping("/hello")
    String hello() {
        return "Hello from GraalVM Native Image";
    }

    public static void main(String&#91;] args) {
        SpringApplication.run(ColdStartApplication.class, args);
    }
}</code></pre>



<h3 class="wp-block-heading">Adım 2: Maven plugin’lerini kontrol edin</h3>



<p class="wp-block-paragraph">Spring Boot parent’ın sağladığı <code>native</code> profilinden yararlanmak için build bölümünde şu plugin’ler bulunmalıdır:</p>



<pre class="wp-block-code"><code>&lt;build&gt;
    &lt;plugins&gt;
        &lt;plugin&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-maven-plugin&lt;/artifactId&gt;
        &lt;/plugin&gt;

        &lt;plugin&gt;
            &lt;groupId&gt;org.graalvm.buildtools&lt;/groupId&gt;
            &lt;artifactId&gt;native-maven-plugin&lt;/artifactId&gt;
        &lt;/plugin&gt;
    &lt;/plugins&gt;
&lt;/build&gt;</code></pre>



<p class="wp-block-paragraph">Spring AOT işlemi ApplicationContext’i build aşamasında analiz eder; bean tanımlarına ve kapalı dünya varsayımına uygun optimize edilmiş kod ile GraalVM hint dosyaları üretir. Maven çıktıları <code>target/spring-aot</code> altında incelenebilir. (<a href="https://docs.spring.io/spring-boot/3.3/maven-plugin/aot.html">Spring Boot AOT belgeleri</a>)</p>



<h3 class="wp-block-heading">Adım 3: Yerel native executable üretin</h3>



<p class="wp-block-paragraph">Makinede kullandığınız Java sürümüyle uyumlu bir GraalVM dağıtımı ve gerekli native toolchain kurulu olmalıdır. Ardından:</p>



<pre class="wp-block-code"><code>./mvnw -Pnative native:compile</code></pre>



<p class="wp-block-paragraph">Executable varsayılan olarak <code>target</code> dizininde oluşur:</p>



<pre class="wp-block-code"><code>./target/cold-start-demo</code></pre>



<p class="wp-block-paragraph">Servisi doğrulayın:</p>



<pre class="wp-block-code"><code>curl http://localhost:8080/hello</code></pre>



<p class="wp-block-paragraph">Gradle kullanan projelerde GraalVM Native Build Tools plugin’i uygulandıktan sonra eşdeğer komutlar şunlardır:</p>



<pre class="wp-block-code"><code>./gradlew nativeCompile
./build/native/nativeCompile/cold-start-demo</code></pre>



<h3 class="wp-block-heading">Adım 4: Buildpack ile native container image oluşturun</h3>



<p class="wp-block-paragraph">Yerel GraalVM kurulumu yerine Docker ve Cloud Native Buildpacks kullanabilirsiniz:</p>



<pre class="wp-block-code"><code>./mvnw -Pnative spring-boot:build-image</code></pre>



<p class="wp-block-paragraph">Oluşan image’ı çalıştırın:</p>



<pre class="wp-block-code"><code>docker run --rm -p 8080:8080 \
  docker.io/library/cold-start-demo:0.0.1-SNAPSHOT</code></pre>



<p class="wp-block-paragraph">Gradle karşılığı:</p>



<pre class="wp-block-code"><code>./gradlew bootBuildImage</code></pre>



<p class="wp-block-paragraph">Spring Boot, Maven ve Gradle üzerinden Paketo buildpack ile JVM içermeyen native container image üretilmesini destekler. (<a href="https://docs.spring.io/spring-boot/3.4/how-to/native-image/developing-your-first-application.html">Spring Boot native uygulama rehberi</a>)</p>



<p class="wp-block-paragraph">Native Image doğrudan cross-compilation yapmadığından executable hedef işletim sistemi ve CPU mimarisi için üretilmelidir. Örneğin Linux AMD64 ile Linux ARM64 artefact’larını ayrı runner’larda veya uygun multi-platform build altyapısında oluşturmak gerekir.</p>



<h3 class="wp-block-heading">Adım 5: Native test çalıştırın</h3>



<p class="wp-block-paragraph">Normal JVM testleri hızlı geri bildirim sağlar; ancak kapalı dünya varsayımından kaynaklanan sorunları yakalamak için native test de CI sürecine eklenmelidir:</p>



<pre class="wp-block-code"><code>./mvnw -Pnative native:test</code></pre>



<p class="wp-block-paragraph">Gradle için:</p>



<pre class="wp-block-code"><code>./gradlew nativeTest</code></pre>



<p class="wp-block-paragraph">Özellikle şu yolları test edin:</p>



<ul class="wp-block-list">
<li>JSON serialization ve deserialization</li>



<li>Reflection kullanan kod</li>



<li>AOP ve transaction proxy’leri</li>



<li>Dosya, template ve resource bundle erişimi</li>



<li>SSL ve güvenlik sağlayıcıları</li>



<li>ORM entity’leri</li>



<li>Dinamik olarak seçilen implementasyonlar</li>
</ul>



<h3 class="wp-block-heading">Adım 6: Eksik metadata’yı tracing agent ile bulun</h3>



<p class="wp-block-paragraph">Spring’in otomatik hint üretiminin yetmediği eski veya uyumsuz bir kütüphanede uygulamayı önce JVM üzerinde tracing agent ile çalıştırabilirsiniz:</p>



<pre class="wp-block-code"><code>java -Dspring.aot.enabled=true \
  -agentlib:native-image-agent=config-output-dir=target/native-agent \
  -jar target/cold-start-demo.jar</code></pre>



<p class="wp-block-paragraph">Uygulamanın kritik akışlarını çalıştırdıktan sonra oluşan metadata’yı inceleyin. Gerekli dosyalar kontrollü biçimde <code>src/main/resources/META-INF/native-image/</code> altına taşınabilir.</p>



<p class="wp-block-paragraph">Tracing agent yalnızca çalıştırılan yolları görür. Eksiksiz test kapsamı yoksa gerekli metadata’nın bir kısmı yine atlanabilir; üretilen dosyaları körlemesine kabul etmek yerine sadeleştirmek ve doğrulamak gerekir. GraalVM’in agent’ı reflection, proxy, JNI ve resource erişimlerini kaydedebilir. (<a href="https://www.graalvm.org/jdk25/reference-manual/native-image/metadata/AutomaticMetadataCollection/">GraalVM tracing agent rehberi</a>)</p>



<h2 class="wp-block-heading">Üretim ortamı için mimari öneriler</h2>



<p class="wp-block-paragraph">GraalVM Native Image özellikle şu iş yüklerinde güçlü bir seçenektir:</p>



<ul class="wp-block-list">
<li>Trafiği düzensiz serverless fonksiyonlar</li>



<li>Scale-to-zero kullanan servisler</li>



<li>Çok hızlı yatay ölçeklenmesi gereken API’ler</li>



<li>Düşük bellek limitli container’lar</li>



<li>CLI uygulamaları ve kısa ömürlü batch işler</li>



<li>Edge veya yoğun çoklu servis ortamları</li>
</ul>



<p class="wp-block-paragraph">Buna karşılık aşağıdaki durumlarda geleneksel JVM seçeneğini ayrıca benchmark etmek gerekir:</p>



<ul class="wp-block-list">
<li>Günlerce çalışan CPU yoğun servisler</li>



<li>Yoğun dinamik class loading veya plugin mimarisi</li>



<li>Native desteği zayıf üçüncü taraf kütüphaneler</li>



<li>Çok sık değişen runtime bean yapıları</li>



<li>Derleme süresi ve CI kaynak maliyetinin kritik olduğu projeler</li>
</ul>



<p class="wp-block-paragraph">İyi bir cloud-native strateji bütün servisleri zorunlu olarak native’e çevirmek değildir. Cold start ve bellek baskısı yaşayan sınır servislerini native executable olarak çalıştırırken, uzun ömürlü ve throughput odaklı servisleri JVM üzerinde bırakmak daha dengeli olabilir.</p>



<h2 class="wp-block-heading">Sonuç</h2>



<p class="wp-block-paragraph">GraalVM Native Image, Java’nın serverless ve mikroservis mimarilerindeki en görünür dezavantajlarından ikisini doğrudan hedefler: cold start gecikmesi ve yüksek memory footprint.</p>



<p class="wp-block-paragraph">AOT derleme; erişilebilir kodu, gerekli runtime bileşenlerini ve belirli başlangıç durumlarını build aşamasında native executable içine taşır. Böylece uygulama hızlı açılır, warm-up ihtiyacı azalır ve daha küçük bir çalışma zamanı belleğiyle hizmet verebilir.</p>



<p class="wp-block-paragraph">Bu kazanımın karşılığında closed-world assumption nedeniyle reflection, dynamic proxy, serialization ve classpath kaynaklarının daha bilinçli yönetilmesi gerekir. Spring Boot 3’ün AOT altyapısı bu işin büyük bölümünü otomatikleştirir; <code>RuntimeHints</code>, reachability metadata, tracing agent ve native testler ise kalan boşlukları kapatır.</p>



<p class="wp-block-paragraph">Doğru karar için son adım her zaman aynıdır: JVM ve native sürümü gerçek trafik profiliniz, hedef container limitleriniz ve ölçeklenme modeliniz altında ölçün. Cold start kritikse GraalVM Native Image çoğu zaman yalnızca bir optimizasyon değil, doğrudan mimari bir avantajdır.</p>



<h2 class="wp-block-heading">Sık Sorulan Sorular</h2>



<h3 class="wp-block-heading">GraalVM Native Image tamamen JVM’siz mi çalışır?</h3>



<p class="wp-block-paragraph">Üretilen executable’ın yanında ayrı bir HotSpot JVM bulunmaz. Ancak binary; garbage collector, thread desteği ve gerekli çalışma zamanı servisleri gibi Native Image runtime bileşenlerini içerir.</p>



<h3 class="wp-block-heading">Native Image her Java uygulamasında daha hızlı mıdır?</h3>



<p class="wp-block-paragraph">Hayır. Başlangıç süresi genellikle çok daha iyidir; fakat uzun süre çalışan ve CPU yoğun uygulamalarda JIT daha yüksek tepe throughput sağlayabilir. Karar benchmark sonucuna göre verilmelidir.</p>



<h3 class="wp-block-heading">Spring Boot 3 reflection sorunlarını otomatik çözer mi?</h3>



<p class="wp-block-paragraph">Spring AOT birçok yaygın kullanım için gerekli hint’leri otomatik üretir. Uygulamaya özel reflection, dinamik proxy veya üçüncü taraf kütüphane kullanımlarında ek runtime hint gerekebilir.</p>



<h3 class="wp-block-heading">Native executable bir kez üretilip her platformda çalıştırılabilir mi?</h3>



<p class="wp-block-paragraph">Hayır. Binary hedef işletim sistemi ve CPU mimarisine özeldir. Linux, macOS, AMD64 ve ARM64 hedefleri için uygun ortamlarda ayrı build alınmalıdır.</p>



<h3 class="wp-block-heading">GraalVM Native Image serverless maliyetini azaltır mı?</h3>



<p class="wp-block-paragraph">Daha hızlı startup ve düşük bellek tüketimi, platformun fiyatlandırma modeline bağlı olarak süre ve kaynak maliyetini azaltabilir. Kesin kazanç ancak gerçek provider, trafik ve bellek ayarlarıyla yapılan ölçüm sonucunda belirlenebilir.</p>



<p class="wp-block-paragraph"></p>
<p><a href="https://alikarahisar.com/blog/graalvm-native-image-cold-start-spring-boot-3/">GraalVM Native Image ile Serverless ve Mikroservislerde Cold Start Sorununu Çözmek</a> yazısı ilk önce <a href="https://alikarahisar.com/blog">Ali Karahisar</a> üzerinde ortaya çıktı.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://alikarahisar.com/blog/graalvm-native-image-cold-start-spring-boot-3/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Garbage Collector Seçimi ve Akort Edilmesi: G1 GC vs. ZGC</title>
		<link>https://alikarahisar.com/blog/garbage-collector-secimi-g1-gc-vs-zgc/</link>
					<comments>https://alikarahisar.com/blog/garbage-collector-secimi-g1-gc-vs-zgc/#respond</comments>
		
		<dc:creator><![CDATA[Ali Karahisar]]></dc:creator>
		<pubDate>Wed, 02 Sep 2026 19:05:32 +0000</pubDate>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Yazılım]]></category>
		<category><![CDATA[G1 GC]]></category>
		<category><![CDATA[Garbage Collector seçimi]]></category>
		<category><![CDATA[GC pause time]]></category>
		<category><![CDATA[Generational ZGC]]></category>
		<category><![CDATA[Java performans optimizasyonu]]></category>
		<category><![CDATA[JVM flags]]></category>
		<category><![CDATA[JVM tuning]]></category>
		<category><![CDATA[large heap]]></category>
		<category><![CDATA[low latency Java]]></category>
		<category><![CDATA[ZGC]]></category>
		<guid isPermaLink="false">https://alikarahisar.com/blog/?p=39</guid>

					<description><![CDATA[<p>Java uygulamalarında Garbage Collector seçimi yalnızca bellek yönetimiyle ilgili teknik bir ayrıntı değildir. GC; API yanıt sürelerini, işlem hacmini, altyapı maliyetini ve sistemin yoğun trafik altındaki kararlılığını doğrudan etkiler. Özellikle onlarca veya yüzlerce gigabaytlık heap kullanan sistemlerde ortalama GC süresine bakmak yeterli değildir. Kullanıcı deneyimini ve servis seviyesi hedeflerini çoğunlukla p99, p99.9 ve p99.99 gecikmeleri [&#8230;]</p>
<p><a href="https://alikarahisar.com/blog/garbage-collector-secimi-g1-gc-vs-zgc/">Garbage Collector Seçimi ve Akort Edilmesi: G1 GC vs. ZGC</a> yazısı ilk önce <a href="https://alikarahisar.com/blog">Ali Karahisar</a> üzerinde ortaya çıktı.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Java uygulamalarında Garbage Collector seçimi yalnızca bellek yönetimiyle ilgili teknik bir ayrıntı değildir. GC; API yanıt sürelerini, işlem hacmini, altyapı maliyetini ve sistemin yoğun trafik altındaki kararlılığını doğrudan etkiler.</p>



<span id="more-39"></span>



<p class="wp-block-paragraph">Özellikle onlarca veya yüzlerce gigabaytlık heap kullanan sistemlerde ortalama GC süresine bakmak yeterli değildir. Kullanıcı deneyimini ve servis seviyesi hedeflerini çoğunlukla p99, p99.9 ve p99.99 gecikmeleri belirler. Bu nedenle doğru soru “Hangi GC daha hızlı?” değil, “Bu iş yükünün gecikme, throughput ve kaynak tüketimi hedefleri için hangi GC daha uygun?” olmalıdır.</p>



<p class="wp-block-paragraph">G1 GC, dengeli throughput ve öngörülebilir duraklamalar sağlamayı amaçlayan genel kullanıma yönelik bir collector’dır. ZGC ise daha fazla işi uygulamayla eş zamanlı yürüterek çok büyük heap’lerde dahi ultra düşük duraklama sürelerini hedefler.</p>



<p class="wp-block-paragraph">Oracle’ın güncel sınıflandırmasına göre G1 “mostly concurrent”, ZGC ise “fully concurrent” bir collector’dır. Oracle, yanıt süresinin en yüksek öncelik olduğu sistemlerde ZGC’yi başlangıç seçeneği olarak önermektedir. Bununla birlikte ZGC’nin düşük duraklama avantajı, iş yüküne bağlı CPU ve bellek maliyetleriyle birlikte değerlendirilmelidir. <a href="https://docs.oracle.com/en/java/javase/25/gctuning/available-collectors.html" target="_blank" rel="noreferrer noopener">Oracle Java 25 GC seçim rehberi</a></p>



<h2 class="wp-block-heading">Kısa cevap: G1 GC mi, ZGC mi?</h2>



<p class="wp-block-paragraph">Aşağıdaki yaklaşım pratik bir başlangıç noktasıdır:</p>



<ul class="wp-block-list">
<li>Dengeli throughput, makul bellek tüketimi ve yüzlerce milisaniyenin altında tutulmak istenen GC duraklamaları için G1 GC güçlü bir varsayılan seçimdir.</li>



<li>Onlarca ya da yüzlerce gigabaytlık heap, yüksek canlı veri miktarı ve tek haneli milisaniyelik uçtan uca gecikme hedefleri varsa ZGC öncelikli adaydır.</li>



<li>CPU kapasitesi sınırlıysa veya en önemli hedef maksimum işlem hacmiyse, ZGC’nin concurrent çalışma maliyeti mutlaka ölçülmelidir.</li>



<li>GC değişikliği sentetik mikro benchmark sonucuyla değil, üretime benzeyen yük, veri dağılımı ve allocation profiliyle doğrulanmalıdır.</li>
</ul>



<p class="wp-block-paragraph">ZGC düşük gecikme için güçlü bir mimari avantaj sağlar; fakat gerçek zamanlı bir collector değildir ve uygulama için kesin bir SLA garantisi vermez. İşletim sistemi zamanlaması, safepoint işlemleri, JIT derlemesi, disk erişimi, sayfa hataları, kilit rekabeti ve dış servisler de tail latency üretebilir.</p>



<h2 class="wp-block-heading">G1 GC nasıl çalışır?</h2>



<p class="wp-block-paragraph">Garbage-First Garbage Collector, heap’i eşit büyüklükte bölgelere ayırır. Bu bölgeler sabit biçimde Eden, Survivor veya Old olarak ayrılmış değildir; ihtiyaç oldukça farklı roller üstlenebilir.</p>



<p class="wp-block-paragraph">G1; generational, bölge tabanlı, evacuating ve çoğunlukla eş zamanlı çalışan bir collector’dır. Global canlılık analizi gibi pahalı işlerin bir bölümünü uygulama thread’leri çalışırken gerçekleştirir. Buna karşılık canlı nesnelerin seçilmiş bölgelerden başka bölgelere kopyalanması ağırlıklı olarak Stop-The-World duraklamaları içinde yapılır.</p>



<p class="wp-block-paragraph">Oracle’ın tanımına göre G1, belirlenen pause-time hedefine yüksek olasılıkla ulaşmaya çalışır; ancak bu hedef her GC duraklaması için kesin bir üst sınır değildir. <a href="https://docs.oracle.com/en/java/javase/25/gctuning/garbage-first-g1-garbage-collector1.html" target="_blank" rel="noreferrer noopener">Oracle G1 GC mimarisi</a></p>



<h3 class="wp-block-heading">G1 GC döngüsü</h3>



<p class="wp-block-paragraph">G1’in çalışma modeli iki ana dönem halinde düşünülebilir.</p>



<h4 class="wp-block-heading">1. Young-only dönemi</h4>



<p class="wp-block-paragraph">Yeni nesilde biriken nesneler young collection ile toplanır. Yaşamaya devam eden nesneler Survivor bölgelerine veya yaşlarına göre Old Generation’a taşınır.</p>



<p class="wp-block-paragraph">Young collection sırasında uygulama thread’leri durdurulur. G1, bir sonraki young generation boyutunu geçmiş duraklama sürelerinden ve kopyalanan canlı nesne miktarından yararlanarak hesaplar.</p>



<h4 class="wp-block-heading">2. Concurrent marking ve space-reclamation dönemi</h4>



<p class="wp-block-paragraph">Old Generation doluluğu Initiating Heap Occupancy eşiğine ulaştığında G1 concurrent marking döngüsünü başlatır:</p>



<ol start="1" class="wp-block-list">
<li>Bir Concurrent Start young collection yapılır.</li>



<li>Old Generation içindeki erişilebilir nesneler uygulamayla eş zamanlı işaretlenir.</li>



<li>Remark duraklamasında işaretleme sonuçlandırılır.</li>



<li>Cleanup aşamasında bölgelerin canlılık oranları hesaplanır.</li>



<li>Mixed collection’larda young bölgelerle birlikte seçilmiş old bölgeler de boşaltılır.</li>
</ol>



<p class="wp-block-paragraph">“Garbage-First” adı, collector’ın önce en fazla çöp içeren ve en verimli biçimde geri kazanılabilecek bölgeleri seçmesinden gelir.</p>



<h3 class="wp-block-heading">G1’in pause-time modeli</h3>



<p class="wp-block-paragraph"><code>-XX:MaxGCPauseMillis</code> bir zaman sınırı değil, ergonomik bir hedeftir. G1, collection set’e eklenecek bölgelerin sayısını ve young generation büyüklüğünü bu hedefe göre ayarlamaya çalışır.</p>



<p class="wp-block-paragraph">Örneğin 100 milisaniyelik hedef vermek, hiçbir GC duraklamasının 100 milisaniyeyi geçmeyeceği anlamına gelmez. Çok yüksek canlı nesne oranı, ani allocation artışı, reference processing, remembered set taraması veya humongous object yoğunluğu hedefin aşılmasına neden olabilir.</p>



<h3 class="wp-block-heading">Remembered set ve card table maliyeti</h3>



<p class="wp-block-paragraph">G1, bölgeler arasındaki referansları bulabilmek için card table ve remembered set yapılarından yararlanır. Uygulama bir referans alanını değiştirdiğinde write barrier ilgili kartı işaretler. Böylece young collection sırasında bütün Old Generation’ın taranması gerekmez.</p>



<p class="wp-block-paragraph">Bu mekanizma toplama işini sınırlar; ancak uygulama thread’lerine barrier maliyeti, ek metadata tüketimi ve remembered set bakım yükü getirir. Nesne grafiğinde çok yoğun bölge geçişleri bulunan sistemlerde bu maliyet görünür hale gelebilir.</p>



<h3 class="wp-block-heading">Evacuation ve Full GC riski</h3>



<p class="wp-block-paragraph">G1, canlı nesneleri seçilmiş bölgelerden boş alanlara taşıyarak belleği sıkıştırır. Taşıma için yeterli boş bölge kalmazsa evacuation failure oluşabilir.</p>



<p class="wp-block-paragraph">Concurrent marking allocation hızına yetişemez ve yeterli alan geri kazanılamazsa G1, son çare olarak Stop-The-World Full GC gerçekleştirebilir. Büyük heap’lerde bu durum saniyelere ulaşabilen ciddi latency sıçramaları oluşturabilir.</p>



<h3 class="wp-block-heading">Humongous object davranışı</h3>



<p class="wp-block-paragraph">Boyutu bir G1 bölgesinin yarısından büyük olan nesneler “humongous” olarak değerlendirilir ve ardışık bölgelerde tutulur. Büyük byte dizileri, sıkıştırılmış mesajlar, cache blokları ve büyük JSON payload’ları bu kategoriye girebilir.</p>



<p class="wp-block-paragraph">Humongous allocation:</p>



<ul class="wp-block-list">
<li>Heap parçalanmasını artırabilir.</li>



<li>Concurrent marking döngüsünü erken başlatabilir.</li>



<li>Ardışık boş bölge ihtiyacı doğurabilir.</li>



<li>Full GC ve hatta <code>OutOfMemoryError</code> riskini yükseltebilir.</li>
</ul>



<p class="wp-block-paragraph">GC loglarında humongous region sayısı yüksekse ilk çözüm JVM bayrağı eklemek değil, büyük allocation kaynaklarını uygulama seviyesinde incelemek olmalıdır.</p>



<h2 class="wp-block-heading">ZGC nasıl çalışır?</h2>



<p class="wp-block-paragraph">ZGC, ölçeklenebilir ve düşük gecikme odaklı, concurrent bir collector’dır. Marking, reference processing ve nesne relocation gibi pahalı işlerin büyük bölümünü uygulama thread’leri çalışırken gerçekleştirir.</p>



<p class="wp-block-paragraph">Oracle’ın güncel rehberine göre ZGC’nin duraklama süreleri kullanılan heap boyutundan bağımsızdır ve collector birkaç yüz megabayttan 16 TB’a kadar heap’lerde çalışabilir. JDK 25 dokümantasyonu maksimum duraklamaları bir milisaniyenin altında hedefleyen çalışma modelini açıklamaktadır. <a href="https://docs.oracle.com/en/java/javase/24/gctuning/z-garbage-collector.html" target="_blank" rel="noreferrer noopener">Oracle ZGC rehberi</a></p>



<p class="wp-block-paragraph">Buradaki kritik ifade “heap boyutundan bağımsız” olmasıdır. Bu, heap büyüdükçe concurrent toplam iş miktarının hiç artmadığı anlamına gelmez. Artan canlı veri daha fazla CPU zamanı ve daha uzun concurrent cycle gerektirebilir. Mimari avantaj, pahalı işin Stop-The-World penceresine taşınmamasıdır.</p>



<h3 class="wp-block-heading">Generational ZGC ve JDK sürümü</h3>



<p class="wp-block-paragraph">ZGC’nin nesilleri kullanan sürümü JDK 21 ile kullanıma sunuldu. Generational ZGC, çoğu nesnenin genç yaşta öldüğü gözleminden yararlanarak young ve old nesilleri farklı sıklıklarda toplar.</p>



<p class="wp-block-paragraph">Sürüm davranışı önemlidir:</p>



<ul class="wp-block-list">
<li>JDK 21 ve 22’de generational mod <code>-XX:+ZGenerational</code> ile etkinleştirilir.</li>



<li>JDK 23’te generational mod varsayılan hale gelmiştir.</li>



<li>JDK 24 itibarıyla non-generational ZGC kaldırılmış ve <code>ZGenerational</code> seçeneğine ihtiyaç kalmamıştır.</li>
</ul>



<p class="wp-block-paragraph">Dolayısıyla internetten kopyalanan eski bir JVM başlangıç satırı yeni JDK’da geçersiz olabilir. Bayraklar hedef JDK dağıtımı ve sürümü üzerinde doğrulanmalıdır. <a href="https://docs.oracle.com/en/java/javase/25/migrate/significant-changes-jdk-21.html" target="_blank" rel="noreferrer noopener">Oracle JDK 21 değişiklikleri</a> ve <a href="https://docs.oracle.com/en/java/javase/25/migrate/significant-changes-jdk-24.html" target="_blank" rel="noreferrer noopener">Oracle JDK 24 değişiklikleri</a></p>



<h3 class="wp-block-heading">Colored pointers</h3>



<p class="wp-block-paragraph">ZGC’nin temel yapı taşlarından biri colored pointer yaklaşımıdır. Nesne referansları yalnızca bellek adresi taşımaz; marking ve relocation durumuyla ilgili metadata da içerir.</p>



<p class="wp-block-paragraph">Bu bilgi sayesinde JVM bir referansın:</p>



<ul class="wp-block-list">
<li>Geçerli GC görünümüne ait olup olmadığını,</li>



<li>İşaretleme işlemi gerektirip gerektirmediğini,</li>



<li>Nesnenin taşınmış olup olmadığını,</li>



<li>Referansın güncellenmesi gerekip gerekmediğini</li>
</ul>



<p class="wp-block-paragraph">kontrol edebilir.</p>



<p class="wp-block-paragraph">Colored pointer yaklaşımı, ZGC’nin nesneleri uygulama thread’leri çalışırken taşıyabilmesinin önemli nedenlerinden biridir.</p>



<h3 class="wp-block-heading">Load ve store barrier’lar</h3>



<p class="wp-block-paragraph">Uygulama bir nesne referansını okuduğunda veya güncellediğinde JIT tarafından üretilen barrier kodu devreye girebilir. Barrier, referansın GC açısından güvenli ve güncel olmasını sağlar.</p>



<p class="wp-block-paragraph">Bir nesne concurrent relocation sırasında başka bir konuma taşınmışsa barrier doğru konumu bulabilir ve referansı düzeltebilir. Bu nedenle bütün uygulama thread’lerini durdurup heap genelindeki tüm referansları tek seferde güncellemek gerekmez.</p>



<p class="wp-block-paragraph">Generational ZGC ayrıca young ve old nesiller arasındaki referansları verimli biçimde izlemek için optimize edilmiş barrier mekanizmaları kullanır. Bunun karşılığı, uygulamanın normal nesne erişim yollarında belirli bir CPU maliyetidir.</p>



<h3 class="wp-block-heading">Concurrent marking</h3>



<p class="wp-block-paragraph">ZGC, erişilebilir nesneleri uygulama çalışırken işaretler. Kısa Stop-The-World evreleri temel olarak kök durumunu güvenli biçimde yakalamak ve bazı faz geçişlerini koordine etmek için kullanılır.</p>



<p class="wp-block-paragraph">Duraklamalarda bütün canlı nesne kümesini dolaşmak yerine sınırlı ve heap boyutuna doğrudan bağlı olmayan işler yapılması, large heap sistemlerde ZGC’nin en önemli avantajıdır.</p>



<h3 class="wp-block-heading">Concurrent relocation</h3>



<p class="wp-block-paragraph">ZGC, seçilen bölgelerdeki canlı nesneleri uygulama çalışırken taşıyabilir. Uygulama eski konumu gösteren bir referansla karşılaştığında barrier mekanizması erişimi doğru adrese yönlendirir.</p>



<p class="wp-block-paragraph">Böylece:</p>



<ul class="wp-block-list">
<li>Heap concurrent biçimde sıkıştırılabilir.</li>



<li>Uzun bir Stop-The-World compaction aşaması önlenebilir.</li>



<li>Büyük canlı veri kümelerinde pause süresinin heap boyutuyla büyümesi engellenebilir.</li>



<li>Ayrı ve uzun süren klasik Full GC ihtiyacı ortadan kaldırılabilir.</li>
</ul>



<h2 class="wp-block-heading">Large heap sistemlerde ZGC’nin mimari avantajları</h2>



<h3 class="wp-block-heading">1. Duraklama süresi heap boyutuyla ölçeklenmez</h3>



<p class="wp-block-paragraph">G1’de concurrent marking heap genelinde çalışsa da canlı nesnelerin tahliyesi ve collection set işleme gibi önemli işler Stop-The-World aşamalarında yapılır. Heap ve canlı veri büyüdükçe bu aşamaların kuyruk gecikmesine etkisi artabilir.</p>



<p class="wp-block-paragraph">ZGC’de pahalı marking ve relocation işleri concurrent yürütüldüğü için 32 GB’tan 256 GB’a geçiş, GC pause süresini aynı oranda büyütmez. Büyük in-memory cache, risk hesaplama motoru, fiyatlama sistemi, oyun sunucusu veya gerçek zamanlı analitik platformlarında bu özellik belirleyicidir.</p>



<h3 class="wp-block-heading">2. Concurrent compaction</h3>



<p class="wp-block-paragraph">Low-pause collector’ların yalnızca erişilemeyen nesneleri bulması yetmez; fragmentation sorununu da uzun duraklama üretmeden çözmesi gerekir. ZGC’nin concurrent relocation yaklaşımı bellek sıkıştırmayı uygulama çalışırken gerçekleştirebilir.</p>



<h3 class="wp-block-heading">3. Daha düşük tail-latency riski</h3>



<p class="wp-block-paragraph">Ortalama yanıt süresi iyi olsa bile uzun bir Full GC p99.9 değerini bozabilir. ZGC’nin tasarımı bu tür büyük GC kaynaklı sıçramaları azaltmaya yöneliktir.</p>



<p class="wp-block-paragraph">Bununla birlikte “GC pause düştü” sonucu, uçtan uca latency’nin otomatik olarak düştüğü anlamına gelmez. Concurrent GC thread’leri CPU üzerinde uygulamayla rekabet ederse servis süresi artabilir. Bu nedenle GC pause, CPU utilization ve request latency birlikte izlenmelidir.</p>



<h3 class="wp-block-heading">4. Allocation spike’larına karşı heap headroom</h3>



<p class="wp-block-paragraph">ZGC concurrent çalıştığı için GC bir cycle’ı tamamlayana kadar uygulama yeni nesne ayırmaya devam eder. Maksimum heap; canlı veri kümesini, cycle boyunca yapılacak allocation’ları ve trafik sıçramalarını karşılayacak boşluğu içermelidir.</p>



<p class="wp-block-paragraph">Örneğin canlı veri kümesi 40 GB olan bir servise yalnızca 42 GB heap vermek, teorik olarak yeterli görünse de yüksek allocation rate altında allocation stall yaratabilir. ZGC tuning’in en önemli parametresi çoğu zaman karmaşık bir bayrak değil, doğru <code>-Xmx</code> ve yeterli headroom’dur.</p>



<h3 class="wp-block-heading">5. Adaptif generational yönetim</h3>



<p class="wp-block-paragraph">Modern ZGC, generational yapıda nesil boyutlarını, GC thread sayısını ve tenuring eşiklerini iş yüküne göre dinamik biçimde ayarlar. Bu nedenle başlangıç yaklaşımı az sayıda bayrak kullanmak ve yalnızca ölçüm sonucunda müdahale etmek olmalıdır.</p>



<h2 class="wp-block-heading">G1 GC ve ZGC çalışma mekanizmalarının karşılaştırması</h2>



<figure class="wp-block-table"><table class="has-fixed-layout"><tbody><tr><th>Teknik özellik</th><th>G1 GC</th><th>ZGC</th></tr><tr><td>Temel hedef</td><td>Throughput ve makul pause süresi dengesi</td><td>Ultra düşük GC pause ve ölçeklenebilirlik</td></tr><tr><td>Çalışma modeli</td><td>Mostly concurrent</td><td>Pahalı GC işlerinde fully concurrent yaklaşım</td></tr><tr><td>Heap organizasyonu</td><td>Region tabanlı, generational</td><td>Region tabanlı, JDK 24+ yalnızca generational</td></tr><tr><td>Marking</td><td>Büyük ölçüde concurrent; başlangıç, remark ve cleanup duraklamaları içerir</td><td>Büyük ölçüde concurrent, kısa faz geçişleri içerir</td></tr><tr><td>Nesne taşıma</td><td>Collection set evacuation işlemi Stop-The-World</td><td>Relocation büyük ölçüde concurrent</td></tr><tr><td>Referans takibi</td><td>Card table, remembered set ve write barrier</td><td>Colored pointers ile load/store barrier’lar</td></tr><tr><td>Pause hedefi</td><td><code>MaxGCPauseMillis</code> ile ergonomik hedef</td><td>Heap boyutundan bağımsız, çok kısa pause tasarımı</td></tr><tr><td>Full GC riski</td><td>Allocation veya evacuation failure durumunda uzun Full GC oluşabilir</td><td>Klasik uzun Full GC ihtiyacını concurrent relocation ile önlemeyi amaçlar</td></tr><tr><td>Throughput</td><td>Genellikle daha yüksek</td><td>Concurrent çalışma ve barrier’lar nedeniyle daha düşük olabilir</td></tr><tr><td>CPU gereksinimi</td><td>Dengeli; pause sırasında yüksek paralellik</td><td>GC cycle boyunca uygulamayla paylaşılacak CPU headroom ister</td></tr><tr><td>Heap headroom ihtiyacı</td><td>Orta; reserve alan yine kritiktir</td><td>Concurrent cycle sırasında allocation için daha yüksek boşluk gerekebilir</td></tr><tr><td>Large heap uygunluğu</td><td>Onlarca GB ve üzeri heap’lerde kullanılabilir</td><td>Yüzlerce GB ve TB ölçeğinde özellikle güçlüdür</td></tr><tr><td>Tuning yaklaşımı</td><td>Pause hedefi, IHOP, reserve ve region davranışı incelenebilir</td><td>Öncelikle heap boyutu ve allocation headroom ayarlanır</td></tr><tr><td>Operasyonel olgunluk</td><td>Uzun süredir varsayılan ve yaygın</td><td>Üretime hazır; sürüme özgü generational geçişi dikkate alınmalı</td></tr></tbody></table></figure>



<h2 class="wp-block-heading">Prod ortamında GC seçim kriterleri</h2>



<figure class="wp-block-table"><table class="has-fixed-layout"><tbody><tr><td>Kriter</td><td>G1 GC lehine durum</td><td>ZGC lehine durum</td><td>Ölçülmesi gereken metrik</td></tr><tr><td>Gecikme SLO’su</td><td>50–300 ms düzeyindeki GC duraklamaları tolere edilebiliyor</td><td>Tek haneli milisaniyelik tail-latency kritik</td><td>p99, p99.9, maksimum pause</td></tr><tr><td>Heap büyüklüğü</td><td>Orta ve büyük heap</td><td>Çok büyük heap veya yüksek live-set</td><td>Heap occupancy, live-set</td></tr><tr><td>Throughput</td><td>Maksimum işlem hacmi öncelikli</td><td>Bir miktar throughput maliyeti kabul edilebilir</td><td>İşlem/saniye, CPU/request</td></tr><tr><td>Allocation rate</td><td>Düşük veya orta</td><td>Yüksek fakat yeterli CPU ve heap headroom mevcut</td><td>MB/s veya GB/s allocation</td></tr><tr><td>CPU kapasitesi</td><td>CPU limiti sıkı</td><td>Concurrent GC için rezerv CPU bulunuyor</td><td>CPU saturation, steal time</td></tr><tr><td>Bellek maliyeti</td><td>Sıkı bellek limiti</td><td>Latency için daha fazla headroom ayrılabilir</td><td>RSS, committed heap, native memory</td></tr><tr><td>Trafik yapısı</td><td>Öngörülebilir ve dengeli</td><td>Ani allocation artışları yaşanıyor</td><td>Allocation spike, burst süresi</td></tr><tr><td>Büyük nesneler</td><td>Humongous allocation düşük</td><td>Büyük nesneler G1’de fragmentation yaratıyor</td><td>Humongous region sayısı</td></tr><tr><td>Konteyner limiti</td><td>Küçük ve sıkı pod limitleri</td><td>Büyük, CPU garantili instance veya pod</td><td>cgroup memory/CPU limitleri</td></tr><tr><td>Operasyonel deneyim</td><td>Ekip G1 loglarını ve davranışını iyi tanıyor</td><td>Ekip ZGC benchmark ve kapasite testlerini yapabiliyor</td><td>Incident ve tuning tecrübesi</td></tr><tr><td>JDK sürümü</td><td>Eski veya kurumsal olarak standartlaştırılmış sürüm</td><td>Güncel LTS veya JDK 24+ kullanılabiliyor</td><td>Dağıtım, sürüm ve flag uyumluluğu</td></tr></tbody></table></figure>



<h2 class="wp-block-heading">Temel JVM bayrakları ve tuning örnekleri</h2>



<p class="wp-block-paragraph">Aşağıdaki değerler doğrudan üretime kopyalanacak reçeteler değildir. Heap büyüklüğü, CPU sayısı, NUMA yapısı, container limitleri ve allocation profili test edilmeden kesinleştirilmemelidir.</p>



<h3 class="wp-block-heading">G1 GC için güvenli başlangıç profili</h3>



<pre class="wp-block-code"><code>java \
  -XX:+UseG1GC \
  -Xms32g \
  -Xmx32g \
  -XX:MaxGCPauseMillis=100 \
  -XX:+AlwaysPreTouch \
  -Xlog:gc*,safepoint:file=/var/log/app/gc.log:time,uptime,level,tags:filecount=10,filesize=100M \
  -jar application.jar</code></pre>



<p class="wp-block-paragraph">Bayrakların amacı:</p>



<ul class="wp-block-list">
<li><code>-XX:+UseG1GC</code>: G1 collector’ı etkinleştirir.</li>



<li><code>-Xms32g -Xmx32g</code>: Heap resizing ve çalışma anındaki sayfa tahsis sürprizlerini azaltır.</li>



<li><code>-XX:MaxGCPauseMillis=100</code>: G1’e 100 ms’lik ergonomik pause hedefi verir.</li>



<li><code>-XX:+AlwaysPreTouch</code>: Heap sayfalarını başlangıçta fiziksel belleğe dokundurur; açılışı uzatabilir fakat runtime latency varyansını azaltabilir.</li>



<li><code>-Xlog:gc*,safepoint</code>: GC ve safepoint davranışını analiz etmeyi sağlar.</li>
</ul>



<p class="wp-block-paragraph">G1 için young generation’ı <code>-Xmn</code> ile sabitlemek genellikle önerilmez. G1, pause hedefine ulaşmak için young generation boyutunu adaptif biçimde kullanır; sabit young generation bu kontrol mekanizmasını zayıflatabilir. Oracle’ın önerisi, önce yalnızca heap boyutu ve gerekiyorsa pause hedefiyle başlamaktır. <a href="https://docs.oracle.com/en/java/javase/22/gctuning/garbage-first-garbage-collector-tuning.html">Oracle G1 tuning rehberi</a></p>



<h3 class="wp-block-heading">G1’de Full GC riski görülüyorsa</h3>



<p class="wp-block-paragraph">GC loglarında <code>Pause Full (G1 Compaction Pause)</code> veya evacuation failure görülmesi durumunda aşağıdaki seçenekler kontrollü olarak değerlendirilebilir:</p>



<pre class="wp-block-code"><code>-XX:G1ReservePercent=20
-XX:ConcGCThreads=8</code></pre>



<p class="wp-block-paragraph"><code>G1ReservePercent</code>, allocation ve evacuation için daha fazla güvenlik alanı bırakabilir. <code>ConcGCThreads</code>, marking’in allocation hızına yetişemediği doğrulandıysa artırılabilir. Her ikisi de maliyetsiz değildir: kullanılabilir heap alanını veya uygulamaya kalan CPU’yu azaltabilir.</p>



<p class="wp-block-paragraph">Adaptive IHOP tahminleri iş yükündeki ani değişikliklere yetişemiyorsa, son çare olarak işaretlemenin daha erken başlatılması denenebilir:</p>



<pre class="wp-block-code"><code>-XX:-G1UseAdaptiveIHOP
-XX:InitiatingHeapOccupancyPercent=30</code></pre>



<p class="wp-block-paragraph">Bu ayar statik davranış üretir. Sezonsal veya burst karakterli iş yüklerinde adaptif mekanizmadan daha kötü sonuç verebileceği için uzun süreli karşılaştırmalı test gerektirir.</p>



<h3 class="wp-block-heading">Çok sayıda yinelenen String için</h3>



<p class="wp-block-paragraph">Heap dump ve profiler, yüksek miktarda yinelenen <code>String</code> bulunduğunu gösteriyorsa:</p>



<pre class="wp-block-code"><code>-XX:+UseStringDeduplication</code></pre>



<p class="wp-block-paragraph">Bu seçenek bellek tüketimini azaltabilir; ancak concurrent deduplication işleminin CPU maliyeti vardır. Kanıt olmadan varsayılan tuning listesine eklenmemelidir.</p>



<h3 class="wp-block-heading">JDK 24+ ZGC düşük gecikme profili</h3>



<pre class="wp-block-code"><code>java \
  -XX:+UseZGC \
  -Xms64g \
  -Xmx64g \
  -XX:+AlwaysPreTouch \
  -Xlog:gc*,safepoint:file=/var/log/app/gc.log:time,uptime,level,tags:filecount=10,filesize=100M \
  -jar application.jar</code></pre>



<p class="wp-block-paragraph"><code>-Xms</code> ile <code>-Xmx</code> değerlerinin eşitlenmesi:</p>



<ul class="wp-block-list">
<li>Heap’in çalışma sırasında büyüme ihtiyacını azaltır.</li>



<li>ZGC’nin belleği işletim sistemine iade etmesini fiilen sınırlar.</li>



<li>Commit/uncommit ve page fault kaynaklı latency varyansını azaltabilir.</li>



<li>Başlangıç süresini ve kalıcı bellek tüketimini artırır.</li>
</ul>



<p class="wp-block-paragraph">Ultra düşük gecikme hedeflerinde bu değiş tokuş çoğu zaman anlamlıdır; bellek yoğunluğu öncelikli ortamlarda ise esnek heap daha uygun olabilir.</p>



<h3 class="wp-block-heading">Esnek bellek kullanan ZGC profili</h3>



<pre class="wp-block-code"><code>java \
  -XX:+UseZGC \
  -Xms16g \
  -Xmx64g \
  -XX:SoftMaxHeapSize=48g \
  -XX:ZUncommitDelay=300 \
  -Xlog:gc*,safepoint:file=/var/log/app/gc.log:time,uptime,level,tags:filecount=10,filesize=100M \
  -jar application.jar</code></pre>



<p class="wp-block-paragraph"><code>SoftMaxHeapSize</code>, ZGC’nin normal koşullarda altında kalmaya çalışacağı yumuşak sınırdır. Collector, allocation stall riskini azaltmak için gerekirse <code>-Xmx</code> değerine kadar büyüyebilir.</p>



<p class="wp-block-paragraph">Örnekte ZGC 48 GB altında kalmaya çalışır; ancak trafik sıçramasında 64 GB’a kadar çıkabilir. Bu, bellek maliyetiyle burst dayanıklılığı arasında kullanışlı bir denge sağlar.</p>



<h3 class="wp-block-heading">JDK 21 ve 22’de Generational ZGC</h3>



<pre class="wp-block-code"><code>java \
  -XX:+UseZGC \
  -XX:+ZGenerational \
  -Xms64g \
  -Xmx64g \
  -XX:+AlwaysPreTouch \
  -jar application.jar</code></pre>



<p class="wp-block-paragraph">Bu örnekteki <code>-XX:+ZGenerational</code> yalnızca ilgili eski sürüm davranışı için kullanılmalıdır. JDK 24 ve sonrasında bu seçenek kaldırılmıştır.</p>



<h3 class="wp-block-heading">ZGC’de thread sayısını değiştirmek</h3>



<p class="wp-block-paragraph">ZGC, GC thread sayısını adaptif olarak ayarlayabilir. Bu nedenle <code>-XX:ConcGCThreads</code> başlangıç profilinde sabitlenmemelidir.</p>



<p class="wp-block-paragraph">GC allocation hızına yetişemiyor, allocation stall oluşuyor ve sistemde boş CPU bulunuyorsa kontrollü bir deney yapılabilir:</p>



<pre class="wp-block-code"><code>-XX:ConcGCThreads=12</code></pre>



<p class="wp-block-paragraph">Değer çok düşükse collector geride kalabilir. Çok yüksekse GC, uygulama thread’lerinin CPU’sunu tüketerek request latency ve throughput’u bozabilir.</p>



<h3 class="wp-block-heading">Explicit large pages</h3>



<p class="wp-block-paragraph">Büyük ve sabit heap’lerde, işletim sistemi önceden doğru biçimde hazırlanmışsa:</p>



<pre class="wp-block-code"><code>-XX:+UseLargePages</code></pre>



<p class="wp-block-paragraph">Large pages, TLB baskısını azaltarak throughput ve latency’ye yardımcı olabilir. Ancak host üzerinde yeterli sayıda sayfa ayrılmadan yalnızca JVM bayrağını eklemek yeterli değildir.</p>



<p class="wp-block-paragraph">Transparent Huge Pages ile explicit large pages aynı şey değildir. Oracle, latency-sensitive ZGC sistemlerinde Transparent Huge Pages kullanımının istenmeyen latency sıçramaları oluşturabileceğini belirtmektedir. Bu nedenle kernel ayarı, JVM konfigürasyonu ve gerçek workload birlikte test edilmelidir. <a href="https://docs.oracle.com/en/java/javase/24/gctuning/z-garbage-collector.html">Oracle ZGC large pages açıklaması</a></p>



<h2 class="wp-block-heading">GC loglarında hangi sinyaller izlenmeli?</h2>



<p class="wp-block-paragraph">Collector seçiminden bağımsız olarak aşağıdaki metrikler birlikte değerlendirilmelidir:</p>



<ul class="wp-block-list">
<li>GC pause p50, p95, p99 ve maksimum değerleri</li>



<li>Young, mixed, old ve full collection sayısı</li>



<li>GC cycle süresi ve cycle sıklığı</li>



<li>Allocation rate</li>



<li>Promotion rate</li>



<li>Collection sonrası heap occupancy</li>



<li>Canlı veri kümesi büyüklüğü</li>



<li>Allocation stall sayısı ve süresi</li>



<li>Application throughput</li>



<li>CPU kullanımı ve CPU throttling</li>



<li>Container memory limiti ile toplam RSS farkı</li>



<li>Safepoint süresi ve safepoint’e ulaşma gecikmesi</li>



<li>Reference processing süresi</li>



<li>G1 humongous region ve evacuation failure sayısı</li>
</ul>



<p class="wp-block-paragraph">Yalnızca “GC toplam süresi” metriğini izlemek yanıltıcıdır. ZGC’de kısa duraklamalara rağmen concurrent GC CPU maliyeti artabilir. G1’de toplam GC süresi makul görünürken tek bir Full GC tail-latency SLO’sunu ihlal edebilir.</p>



<h2 class="wp-block-heading">Üretime geçiş için önerilen test yöntemi</h2>



<p class="wp-block-paragraph">GC değişimi kontrollü bir performans mühendisliği çalışması olarak ele alınmalıdır:</p>



<ol start="1" class="wp-block-list">
<li>Mevcut collector ile temel ölçüm alın.</li>



<li>Aynı JDK dağıtımı, heap ve CPU limitleriyle ikinci collector’ı test edin.</li>



<li>Gerçekçi canlı veri kümesini test başlamadan önce oluşturun.</li>



<li>JVM ısınması ve JIT derlemesi tamamlandıktan sonra ölçüm alın.</li>



<li>Ortalama yükün yanı sıra trafik sıçraması ve uzun süreli soak test uygulayın.</li>



<li>p99.9 latency, throughput, CPU ve RSS değerlerini birlikte karşılaştırın.</li>



<li>Heap dump, JFR ve GC loglarını allocation kaynakları açısından inceleyin.</li>



<li>Canary deployment ile sınırlı üretim trafiğinde doğrulayın.</li>



<li>Otomatik rollback eşiğini GC pause yerine uçtan uca servis SLO’suna bağlayın.</li>
</ol>



<p class="wp-block-paragraph">Konteyner ortamında yalnızca <code>-Xmx</code> değerini pod bellek limitine eşitlemeyin. Metaspace, code cache, thread stack’leri, direct buffer’lar, JNI kütüphaneleri ve JVM’nin native yapıları da RSS tüketir. Heap dışında yeterli güvenlik payı bırakılmalıdır.</p>



<h2 class="wp-block-heading">Hangi senaryoda hangi GC seçilmeli?</h2>



<h3 class="wp-block-heading">G1 GC tercih edin:</h3>



<ul class="wp-block-list">
<li>Gecikme hedefi yüzlerce milisaniye ölçeğindeyse,</li>



<li>Throughput, ultra düşük pause süresinden daha önemliyse,</li>



<li>CPU limiti sıkıysa,</li>



<li>Heap orta veya büyük olsa da canlı veri kümesi yönetilebiliyorsa,</li>



<li>Operasyon ekibi yerleşik ve yaygın bir collector istiyorsa,</li>



<li>Gerçek workload altında G1 Full GC veya evacuation problemi üretmiyorsa.</li>
</ul>



<h3 class="wp-block-heading">ZGC tercih edin:</h3>



<ul class="wp-block-list">
<li>p99.9 ve üzeri tail-latency kritikse,</li>



<li>Tek haneli milisaniyelik servis hedefleri bulunuyorsa,</li>



<li>Heap onlarca veya yüzlerce gigabaytsa,</li>



<li>Canlı veri kümesi büyükse,</li>



<li>Uzun G1 evacuation ya da Full GC duraklamaları görülüyorsa,</li>



<li>Concurrent GC için CPU kapasitesi ayrılabiliyorsa,</li>



<li>Heap’e yeterli allocation headroom verilebiliyorsa.</li>
</ul>



<h2 class="wp-block-heading">Sonuç</h2>



<p class="wp-block-paragraph">G1 GC ve ZGC arasında evrensel bir kazanan yoktur. G1 GC, yüksek throughput ile nispeten kısa ve tahmin edilebilir duraklamalar arasında güçlü bir denge sağlar. Bu nedenle çoğu kurumsal Java servisi için hâlâ iyi bir başlangıç noktasıdır.</p>



<p class="wp-block-paragraph">ZGC’nin ayrıştığı alan, büyük heap ve katı tail-latency hedefleridir. Colored pointers, barrier mekanizmaları, concurrent marking ve concurrent relocation sayesinde pahalı GC işlemlerinin büyük bölümünü uygulamayı durdurmadan gerçekleştirir. Böylece heap büyüklüğünün Stop-The-World süresine etkisini önemli ölçüde azaltır.</p>



<p class="wp-block-paragraph">Bunun karşılığında ZGC’ye yeterli CPU ve heap headroom sağlanmalıdır. Collector seçimi yalnızca maksimum GC pause karşılaştırmasına indirgenmemeli; throughput, p99.9 yanıt süresi, allocation rate, CPU tüketimi, RSS ve altyapı maliyeti birlikte değerlendirilmelidir.</p>



<p class="wp-block-paragraph">Kurumsal üretim ortamı için en güvenilir yaklaşım şudur: Önce sade bir JVM konfigürasyonu kurun, ölçüm alın, üretime benzeyen yük altında G1 ve ZGC’yi karşılaştırın ve yalnızca gözlemlenmiş bir problemi çözen bayrağı ekleyin.</p>
<p><a href="https://alikarahisar.com/blog/garbage-collector-secimi-g1-gc-vs-zgc/">Garbage Collector Seçimi ve Akort Edilmesi: G1 GC vs. ZGC</a> yazısı ilk önce <a href="https://alikarahisar.com/blog">Ali Karahisar</a> üzerinde ortaya çıktı.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://alikarahisar.com/blog/garbage-collector-secimi-g1-gc-vs-zgc/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
