<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Compiler Blog - Software Engineering Blog]]></title><description><![CDATA[Programming articles, web development, advanced software topics and everything else about software engineering!]]></description><link>https://compiler.blog</link><generator>RSS for Node</generator><lastBuildDate>Sun, 12 Apr 2026 16:04:46 GMT</lastBuildDate><atom:link href="https://compiler.blog/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Software Engineering Levels]]></title><description><![CDATA[What are the differences between junior, senior, and lead engineers different? Junior vs. Senior developer?
Junior engineers are expected to solve complex problems with guidance. They need practical experience to get better at problem-solving. They'r...]]></description><link>https://compiler.blog/software-engineering-levels</link><guid isPermaLink="true">https://compiler.blog/software-engineering-levels</guid><category><![CDATA[Programming Blogs]]></category><category><![CDATA[software development]]></category><category><![CDATA[Beginner Developers]]></category><dc:creator><![CDATA[Ozan Akman]]></dc:creator><pubDate>Sun, 19 May 2024 11:15:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/QPRd_UC3nE4/upload/a85d67f650644d6bc0ac63623847ad6e.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>What are the differences between junior, senior, and lead engineers different? Junior vs. Senior developer?</p>
<p><strong>Junior</strong> engineers are expected to solve complex problems with guidance. They need practical experience to get better at problem-solving. They're not yet fluent in coding but are expected to get better at it, too.</p>
<p><strong>Senior</strong> engineers are expected to solve complex business problems. They are already fluent in coding skills and understand it's all about problem-solving. They know that coding is one of the tools used to solve actual problems. The focus shifts from tools, libraries, and frameworks to business. They don't only know about coding, but they also know about software design and architecture. They can design systems that will last long and keep maintenance costs low. Senior engineers take ownership, mentor others, and communicate with other teams to achieve business goals. Your team needs to perform at its best? That's also a problem for you to solve; it's not only about coding. The goal is to make the team a better place.</p>
<p><strong>Lead</strong> engineers are expected to solve complex business problems and comprehensively understand the business. They should create an open space where other developers can freely discuss and contribute to the business. They're also expected to develop initiatives to improve business and technical quality. They observe the other teams, understand their processes, and take steps to improve them. They join teams performing poorly and leave those teams functioning at their best, making their own presence obsolete.</p>
<p><strong>Regardless of your level, you're expected to solve complex problems.</strong> You're more than just a bridge between the software world and product. You're an integral part of the product development cycle. You are supposed to do both discovery and delivery. You're not just solving computer problems; you're solving human problems. Computers don't have problems like buying a flight ticket, booking a hotel room, booking a taxi, or finding a nearby plumber. Those are the actual problems we're solving. We can only contribute to smoothing these processes if we understand the problems and the people for whom we are solving them.</p>
<p>To advance in your career, try to understand business and other stakeholders. Fixing code problems alone won't achieve that. You need to get out of your shell and take ownership. Help make the product a little better than it was yesterday.</p>
<p>Doing what you're expected to do is not the best way to get a promotion. You're supposed to do exceptional for a while to prove that you're ready for the next level!</p>
]]></content:encoded></item><item><title><![CDATA[Testing Best Practices: The Ultimate Guide]]></title><description><![CDATA[Introduction
Hey there! Today, we’re going to explore the testing best practices. These little tips will make your life as a developer easier. I guarantee they will also help you understand testing better. I’ve spent roughly 10 years in the industry ...]]></description><link>https://compiler.blog/testing-best-practices-the-ultimate-guide</link><guid isPermaLink="true">https://compiler.blog/testing-best-practices-the-ultimate-guide</guid><category><![CDATA[Programming Blogs]]></category><category><![CDATA[Beginner Developers]]></category><category><![CDATA[software development]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Ozan Akman]]></dc:creator><pubDate>Thu, 07 Mar 2024 11:24:53 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1709808841527/16d7bdc7-49dd-4256-a02e-d7cfe7518d50.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>Hey there! Today, we’re going to explore the testing best practices. These little tips will make your life as a developer easier. I guarantee they will also help you understand testing better. I’ve spent roughly 10 years in the industry and have been a part of various projects with different testing approaches. Testing essentially serves as evidence that the business functions as expected. Why do we even need that evidence? Well, how can you be sure otherwise? Maybe you can be sure if your app is just a contact form. But, imagine the enterprise apps, let’s say your app is supposed to handle booking flight tickets. How can you manually test all those processes? There are too many parameters involved. You can’t ensure that you’re not going to break it. Let’s think about a travel booking system, from searching flights to storing customer data, generating tickets, processing payments, sending emails, SMS, and other notifications. What about notifying travel agencies about reserved spots? It’s a complex process that requires careful attention to detail. Apart from that, let’s imagine our favorite programming language released a new version. How would you ensure that updating to the latest version doesn’t break your business? Would you manually test every use case? It’s crucial to rely on automated tests. Let’s understand the practice!</p>
<h2 id="heading-focus-on-behavior">Focus on Behavior</h2>
<p>Developers often make the mistake of getting too caught up in the unimportant details. The purpose of enterprise software is to solve specific problems in an area. For instance, if we’re in the travel industry, our focus should be on solving problems related to searching for tickets, pricing, and booking. Our business makes money by helping its users efficiently book tickets. We want to ensure this process never breaks and we meet all business requirements. The implementation details change from time to time. We use different libraries. We discover another way to implement the same idea, perhaps a more performant one, and refactor some parts. However, the business requirements are still the same. Users still want to book a flight. They don’t care if we use a specific library or some specific SQL database. We could decide to store the same data in JSON files tomorrow. Users don’t care about these types of decisions, and neither should we. Those decisions shouldn’t even remotely affect our use cases.</p>
<p>Imagine we have a business requirement to send a confirmation email when users book a flight ticket. Let’s call it <code>BookingConfirmationListener</code>. I’ve seen a test like this before:</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_listener_is_called</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span> 
</span>{
    $booking = <span class="hljs-built_in">self</span>::createBooking();

    $listenerMock = <span class="hljs-keyword">$this</span>-&gt;createMock(BookingConfirmationListener::class);
    $listenerMock-&gt;expects(<span class="hljs-keyword">$this</span>-&gt;once())
        -&gt;method(<span class="hljs-string">'sendBookingConfirmation'</span>);
    <span class="hljs-built_in">self</span>::setService(BookingConfirmationListener::class, $mock);

    $event = <span class="hljs-keyword">new</span> BookingWasCreated($booking-&gt;id);

    <span class="hljs-built_in">self</span>::dispatchEvent($event);
}
</code></pre>
<p>This test will pass when the <code>sendBookingConfirmation</code> method is called by the dispatched <code>BookingWasCreated</code> event. But, it doesn’t prove anything other than the fact the listener method was called. What if I wanted to rename this listener method? Do I need to update the test case as well? An extra cost of maintainability? What If I wanted to refactor it? Let’s say I changed its interval behavior and made a logical mistake. This test would still pass without catching that bug! It doesn’t add any confidence. We can’t ensure that our changes won’t break anything. We need to avoid these types of tests. We should focus on business requirements.</p>
<p>How can we switch our focus from implementation details to business requirements? Well, let’s rename this test case to be explicit, and let it explain the use case. We also need to add some behavioral characteristics to align with business requirements.</p>
<pre><code class="lang-php"><span class="hljs-comment">#[Test]</span>
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">it_sends_booking_confirmation_email</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span>
</span>{
    <span class="hljs-comment">// Arrange what's needed for this use case.</span>
    $booking = <span class="hljs-built_in">self</span>::createBooking();
    $event = <span class="hljs-keyword">new</span> BookingWasCreated($booking-&gt;id);

    <span class="hljs-comment">// Act</span>
    <span class="hljs-built_in">self</span>::dispatchEvent($event);

    <span class="hljs-comment">// Assert against the business requirement of sending an email notification.</span>
    <span class="hljs-comment">// Did the "BookingWasCreated" event result in dispatching the expected notification?</span>
    $expectedNotification = <span class="hljs-keyword">new</span> BookingConfirmationEmailNotification($booking-&gt;id);
    <span class="hljs-built_in">self</span>::assertTrue(<span class="hljs-built_in">self</span>::hasNotificationBeenDispatched($expectedNotification));
}
</code></pre>
<p>Now, we can rename this listener or method. We can refactor it, even move that class from one place to another. These are implementation details. As long as our system meets the business requirement of producing a booking confirmation email, this test case won’t fail. It doesn’t matter how this requirement is implemented, as long as the behavior remains the same. We have the flexibility to make changes without worrying about breaking the system. If we break it, it will tell us. The test case gives us confidence when refactoring, knowing we can ensure the same behavior even after making changes.</p>
<h2 id="heading-be-explicit">Be Explicit</h2>
<p>Test cases are a list of business requirements. They’re supposed to explain what that part of business should do. We want to make them as clear as possible because when it comes to maintaining that part, these cases will educate us and other developers about the business requirements while ensuring they work as expected. We should name our tests by business specifications, not arbitrary, implicit, technical, or made-up names.</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_is_successful</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span> 
</span>{
  <span class="hljs-comment">// Test code</span>
}
</code></pre>
<p>Is there any possibility of understanding what’s our business specification from looking at this test name? Not really. How could we improve it? Give it a clear name, and simply, describe the scenario and outcome.</p>
<pre><code class="lang-php"><span class="hljs-comment">#[Test]</span>
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">it_stores_booking_confirmation</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span> 
</span>{
    <span class="hljs-comment">// Arrange</span>
    $booking = <span class="hljs-built_in">self</span>::createBooking();
    $command = <span class="hljs-keyword">new</span> StoreBookingConfirmationCommand($booking-&gt;id);

    <span class="hljs-comment">// Act</span>
    <span class="hljs-built_in">self</span>::dispatchCommand($command);

    <span class="hljs-comment">// Assert</span>
    $expectedBookingConfirmation = BookingConfirmation::fromBooking($booking);
    $actualBookingConfirmation = <span class="hljs-built_in">self</span>::getBookingConfirmationRepository()-&gt;get($booking-&gt;id);

    <span class="hljs-built_in">self</span>::assertEquals($expectedBookingConfirmation, $actualBookingConfirmation);
}
</code></pre>
<p>Here is another clean example:</p>
<pre><code class="lang-php"><span class="hljs-comment">#[Test]</span>
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">it_sends_create_account_notification</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span> 
</span>{
    <span class="hljs-comment">// Arrange</span>
    $user = <span class="hljs-built_in">self</span>::createUser();

    <span class="hljs-comment">// Act</span>
    <span class="hljs-built_in">self</span>::dispatchEvent(<span class="hljs-keyword">new</span> UserCreated($user-&gt;id));

    <span class="hljs-comment">// Assert</span>
    $expectedNotification = <span class="hljs-keyword">new</span> CreateAccountNotification($user-&gt;id);
    <span class="hljs-built_in">self</span>::assertNotificationHasBeenDispatched($expectedNotification);
}
</code></pre>
<p>Even without context or explanation, these test cases are easy to understand. Of course, I have deliberately chosen simple examples to prove a point. But, even complex requirements should be approached the same way. Our focus should always be on the behavior. We should name our tests explicitly and keep them as simple as possible based on the behavior.</p>
<h2 id="heading-dont-test-configuration">Don’t Test Configuration</h2>
<p>Configurations are one of the implementation details. You don’t need to focus on implementation details. You don’t need to validate them in isolation. That is already validated when you test against the expected outcome. If your configuration is broken, your use case tests will fail anyway.</p>
<pre><code class="lang-php"><span class="hljs-comment">#[Test]</span>
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">module_configuration_is_correct</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span>
</span>{
   <span class="hljs-comment">// Arrange the test case &amp; act</span>
   $moduleConfig = <span class="hljs-comment">// Get module config from a framework</span>

   <span class="hljs-comment">// Assert</span>
   <span class="hljs-built_in">self</span>::assertTrue($moduleConfig-&gt;has(<span class="hljs-string">'some_parameter'</span>));
   <span class="hljs-built_in">self</span>::assertTrue($moduleConfig-&gt;has(<span class="hljs-string">'validators'</span>));
   <span class="hljs-built_in">self</span>::assertCount(<span class="hljs-number">4</span>, $moduleConfig-&gt;get(<span class="hljs-string">'validators'</span>));
}
</code></pre>
<p>If you encounter a configuration test similar to the one above and already have a behavior test, you can safely delete it. The behavior test should already validate this configuration. Of course, unit tests won’t catch this type of issue. I’d suggest keeping the bare minimum happy path in a more expensive test, such as integration testing. The integration test suite will help you detect this type of low-level configuration issue.</p>
<h2 id="heading-dont-test-simple-values">Don’t Test Simple Values</h2>
<p>You don’t need to write tests for every little detail. Sometimes it’s redundant. We want to put our focus on what’s important. For instance, you don’t need to write a unit test for DataTransferObjects (DTO) or ValueObjects (VO) that will be used and validated by higher-level components. Tests should bring value and confidence to our development workflow. They should help reduce maintenance costs. These types of tests keep you slow when you want to change implementation details. Here is an example of a DataTransferObject (DTO) test:</p>
<pre><code class="lang-php"><span class="hljs-comment">#[Test]</span>
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">successfully_constructs_create_user_dto</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span>
</span>{
    <span class="hljs-comment">// Arrange the test case &amp; act</span>
    $dto = <span class="hljs-keyword">new</span> CreateUserDto(<span class="hljs-string">'username'</span>, <span class="hljs-string">'password'</span>, <span class="hljs-string">'email@example.com'</span>, notificationPreferences: [...]);

    <span class="hljs-comment">// Assert</span>
    <span class="hljs-built_in">self</span>::assertSame(<span class="hljs-string">'username'</span>, $dto-&gt;username);
    <span class="hljs-built_in">self</span>::assertSame(<span class="hljs-string">'password'</span>, $dto-&gt;password);
    <span class="hljs-built_in">self</span>::assertSame(<span class="hljs-string">'email@example.com'</span>, $dto-&gt;email);
    <span class="hljs-built_in">self</span>::assertSame([...], $dto-&gt;notificationPreferences);
}
</code></pre>
<p>We could validate this DTO’s construction in a higher-level test case. We already know DTOs don’t have any behavior. They don’t have any meaning alone. We always use them within another context. Therefore, we don’t need to validate them in isolation. Let’s check out what that means with the following higher-level test:</p>
<pre><code class="lang-php"><span class="hljs-comment">#[Test]</span>
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">it_creates_user</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span>
</span>{
    <span class="hljs-comment">// Arrange</span>
    $dto = <span class="hljs-keyword">new</span> CreateUserDto(<span class="hljs-string">'username'</span>, <span class="hljs-string">'password'</span>, <span class="hljs-string">'email@example.com'</span>, notificationPreferences: [...]);
    $userRepository = <span class="hljs-keyword">new</span> InMemoryUserRepository();
    $passwordHasher = <span class="hljs-keyword">new</span> StubPasswordHasher([
        <span class="hljs-string">'password'</span> =&gt; <span class="hljs-string">'hashedPassword'</span>,
    ]);
    $service = <span class="hljs-keyword">new</span> CreateUserService($userRepository, $passwordHasher);

    <span class="hljs-comment">// Act</span>
    $service-&gt;createUser($dto);

    <span class="hljs-comment">// Assert</span>
    $actualUser = $userRepository-&gt;getByEmail(<span class="hljs-string">'email@example.com'</span>);
    $expectedUser = User::withId($actualUser-&gt;id, <span class="hljs-string">'username'</span>, <span class="hljs-string">'email@example.com'</span>, notificationPreferences: [...]);
    $expectedUser-&gt;setHashedPassword(<span class="hljs-string">'hashedPassword'</span>);

    <span class="hljs-built_in">self</span>::assertEquals($expectedUser, $actualUser);
}
</code></pre>
<p>If the DataTransferObject (DTO) is in an invalid state, our higher-level test case will fail anyway. It will tell us this scenario needs some rework. I wouldn’t say this type of simple object test is completely useless. In some cases, they might become handy to pinpoint issues. However, that’s only true when they have a behavior, such as validation behavior in ValueObjects (VO). Even then, most of the time, we don’t need them. It’s rare to find a value in those tests. Let me explain why. Imagine a similar scenario. But this time, we have a ValueObject (VO) that carries some information. Let’s call it the <code>Address</code> object. Our use case would be adding a new address to an existing user. If we try to construct the <code>Address</code> ValueObject with invalid data, our higher-level test case should fail because the <code>Address</code> object won’t allow itself to be created in an invalid state. Even without having a separate test case for the ValueObject (VO), we can still ensure that a small unit is validated. Another good aspect of focusing on higher-level components is that we can catch invalid behaviors of the <code>Address</code> this way. Let’s say we have a bug in the validation of zip code. Our use case won’t produce the expected outcome. It will fail. We won’t have issues catching the bug that comes from the <code>Address</code> object in the higher-level test.</p>
<h2 id="heading-remove-unnecessary-assertions">Remove Unnecessary Assertions</h2>
<p>Multiple assertions only make sense when they bring value. Mostly, we exaggerate multiple assertions when we could assert against a simple outcome. When we think about our requirements, we think about some scenarios. If a user sends this form, we want to store their data in our database. If a user adds a new post, we want to notify their followers. If a user updates their profile information, we want to update a database record. All those scenarios have some outcome. Our classes and methods don’t necessarily return something, but they cause some effect we can assert against. Image a scenario where we create a user:</p>
<pre><code class="lang-php"><span class="hljs-comment">#[Test]</span>
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">it_creates_user</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span>
</span>{
    <span class="hljs-comment">// Arrange</span>
    <span class="hljs-comment">// ....</span>

    <span class="hljs-comment">// Act</span>
    <span class="hljs-built_in">self</span>::dispatchCommand(<span class="hljs-keyword">new</span> CreateUser(<span class="hljs-string">'User name'</span>, <span class="hljs-string">'test@email.com'</span>, <span class="hljs-keyword">new</span> DateTimeImmutable(<span class="hljs-string">'01.01.1993'</span>)));

    <span class="hljs-comment">// Assert</span>
    <span class="hljs-built_in">self</span>::assertSame(<span class="hljs-string">'User name'</span>, $actualUser-&gt;name);
    <span class="hljs-built_in">self</span>::assertSame(<span class="hljs-string">'test@email.com'</span>, $actualUser-&gt;email);
    <span class="hljs-built_in">self</span>::assertSame(<span class="hljs-keyword">new</span> DateTimeImmutable(<span class="hljs-string">'01.01.1993'</span>), $actualUser-&gt;birthDate);
    <span class="hljs-built_in">self</span>::assertTrue(<span class="hljs-built_in">self</span>::isUserCreated($user-&gt;email));
}
</code></pre>
<p>We can simplify our test case by reducing multiple assertion calls to just one. These assertions are unnecessary because when we validate against the expected outcome, the expected user, we already validate for all those fields. If the expected user is not produced from our use case, the test case will naturally fail.</p>
<pre><code class="lang-php"><span class="hljs-comment">#[Test]</span>
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">it_creates_user</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span>
</span>{
    <span class="hljs-comment">// Arrange the test case &amp; act</span>
    <span class="hljs-comment">// ....</span>

    <span class="hljs-comment">// Assert</span>
    $actualUser = $userRepository-&gt;getByEmail(<span class="hljs-string">'test@email.com'</span>);
    $expectedUser = <span class="hljs-keyword">new</span> User(<span class="hljs-string">'User name'</span>, <span class="hljs-string">'test@email.com'</span>, <span class="hljs-keyword">new</span> DateTimeImmutable(<span class="hljs-string">'27.08.1993'</span>));

    <span class="hljs-built_in">self</span>::assertEquals($expectedUser, $actualUser);
}
</code></pre>
<h2 id="heading-dont-mock-it">Don’t Mock It</h2>
<p>Mocking feels convenient when you start writing tests. It feels like you’re properly isolating your test cases while not putting much effort into creating or understanding the involved parts. That’s exactly why you shouldn’t use any mocking frameworks. You miss out on the “intent” part. Your test cases are supposed to explain your business requirements. Mocks clearly don’t express that intent. They’re tightly coupled to the implementation details.</p>
<p>Let’s understand this tight coupling with an actual case. Imagine the usual forgotten password scenario. We want to generate a link and send it to the user’s inbox. They can use this link to reset their password. We have a simple controller called <code>ResetPasswordController</code> with a simple method <code>sendResetPasswordEmail</code>. We have a service to generate a reset password link called <code>ResetPasswordLinkGenerator</code>. We also have the <code>UserRepository</code> to find information for the given user. Finally, the <code>EmailService</code> itself for actually sending the email.</p>
<p>Now, let’s try to create a unit test for the <code>SendResetPasswordEmailController</code> using mocks. The test case will result in the following:</p>
<pre><code class="lang-php"><span class="hljs-comment">#[Test]</span>
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">it_sends_reset_password_email</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span>
</span>{
    $userMock = <span class="hljs-keyword">$this</span>-&gt;createMock(User::class);

    $resetPasswordLinkGeneratorMock = <span class="hljs-keyword">$this</span>-&gt;createMock(ResetPasswordLinkGenerator::class);
    $resetPasswordLink = <span class="hljs-string">'https://example.com/reset-password'</span>;
    $resetPasswordLinkGeneratorMock-&gt;method(<span class="hljs-string">'generateLink'</span>)
        -&gt;willReturn($resetPasswordLink);

    $userRepositoryMock = <span class="hljs-keyword">$this</span>-&gt;createMock(UserRepository::class);
    $userRepositoryMock-&gt;method(<span class="hljs-string">'findByEmail'</span>)
        -&gt;willReturn($userMock);

    $emailServiceMock = <span class="hljs-keyword">$this</span>-&gt;createMock(EmailService::class);
    $emailServiceMock-&gt;expects(<span class="hljs-keyword">$this</span>-&gt;once())
        -&gt;method(<span class="hljs-string">'send'</span>)
        -&gt;with(
            $userMock-&gt;getEmail(),
            <span class="hljs-string">'Password Reset'</span>,
            <span class="hljs-string">"Click the link to reset your password: <span class="hljs-subst">$resetPasswordLink</span>"</span>
        );

    $controller = <span class="hljs-keyword">new</span> SendResetPasswordEmailController(
        $userRepositoryMock,
        $resetPasswordLinkGeneratorMock,
        $emailServiceMock,
    );

    $controller-&gt;sendResetPasswordEmail(<span class="hljs-string">'user@example.com'</span>);
}
</code></pre>
<p>This is a simple example where mocking is overused. Our codebases often have more complex cases. Those mocks hide the intent. The test case is hard to understand. It doesn’t explain the business requirements. Because those mocks are tightly coupled to the implementation details. Even if one of the mocks is misconfigured, the test case would still pass, which means it is not testing any behavior. It gives us false confidence. Additionally, if any implementation detail changes, it would be a burden to find the mocks that use those details. The tests should focus on behavior rather than implementation details. They should verify the expected outcomes and behaviors, rather than simply asserting that certain methods are called with specific arguments. Use mocks only for testing external dependencies, such as third-party sources.</p>
<p>We could easily improve the readability, maintainability, and intent by introducing test doubles. Let’s have a look together:</p>
<pre><code class="lang-php"><span class="hljs-comment">#[Test]</span>
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">it_sends_reset_password_email</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span>
</span>{
    <span class="hljs-comment">// Arrange</span>
    $user = <span class="hljs-keyword">new</span> User(<span class="hljs-string">'user@example.com'</span>, <span class="hljs-string">'Test User'</span>);
    $userRepository = <span class="hljs-keyword">new</span> InMemoryUserRepository();
    $userRepository-&gt;save($user);    

    $resetPasswordLinkGenerator = <span class="hljs-keyword">new</span> StubResetPasswordLinkGenerator(<span class="hljs-string">'https://example.com/reset-password'</span>)

    $emailService = SpyEmailService();

    $controller = <span class="hljs-keyword">new</span> SendResetPasswordEmailController(
        $userRepository,
        $resetPasswordLinkGenerator,
        $emailService,
    );

    <span class="hljs-comment">// Act</span>
    $controller-&gt;sendResetPasswordEmail(<span class="hljs-string">'user@example.com'</span>);

    <span class="hljs-comment">// Assert</span>
    $expectedEmail = <span class="hljs-keyword">new</span> ResetPasswordEmail(<span class="hljs-string">'user@example.com'</span>);
    <span class="hljs-built_in">self</span>::assertTrue($emailService-&gt;hasEmail($expectedEmail));
}
</code></pre>
<h2 id="heading-remove-if-else-assertions">Remove If Else Assertions</h2>
<p>Each requirement should have its test case. We want to ensure we’re not breaking anything while changing our software. We want to update our system confidently. The problem with <code>if-else</code> assertions comes from their nature of hiding multiple cases in one test. We want to isolate each test case and test a specific use case within its context and specification. Usually, the multiple conditionals tell us that there are different types of behavior hidden in one test case. Let’s have a look at that:</p>
<pre><code class="lang-php"><span class="hljs-comment">#[Test]</span>
<span class="hljs-comment">#[DataProvider('provideNotificationTypes')]</span>
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">it_sends_account_created_notification</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> $type</span>): 
</span>{
    <span class="hljs-comment">// Arrange the test case &amp; act</span>
    <span class="hljs-comment">// ....</span>

    <span class="hljs-keyword">if</span> ($type === <span class="hljs-string">'push'</span>) {
        $expectedNotification = <span class="hljs-keyword">new</span> PushAccountCreatedNotification(...);
        <span class="hljs-built_in">self</span>::assertEquals($expectedNotification, <span class="hljs-built_in">self</span>::getDispatchedNotification());
    } <span class="hljs-keyword">else</span> ($type === <span class="hljs-string">'SMS'</span>) {
        $expectedNotification = <span class="hljs-keyword">new</span> SmsAccountCreatedNotification(...);
        <span class="hljs-built_in">self</span>::assertEquals($expectedNotification, <span class="hljs-built_in">self</span>::getDispatchedNotification());
  }
}
</code></pre>
<p>In this test case, we’re asserting against two different types of notifications. It’s hard to read. Also, it increases the fragility of the case. I would pretty much prefer the following:</p>
<pre><code class="lang-php"><span class="hljs-comment">#[Test]</span>
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">it_sends_sms_account_created_notification</span>(<span class="hljs-params"></span>): 
</span>{
    <span class="hljs-comment">// Arrange the test case &amp; act</span>
    <span class="hljs-comment">// ....</span>

    $expectedNotification = <span class="hljs-keyword">new</span> SmsAccountCreatedNotification(...);

    <span class="hljs-built_in">self</span>::assertSame($expectedNotification, <span class="hljs-built_in">self</span>::getLastDispatchedNotification());
}

<span class="hljs-comment">#[Test]</span>
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">it_sends_push_account_created_notification</span>(<span class="hljs-params"></span>): 
</span>{
    <span class="hljs-comment">// Arrange the test case &amp; act</span>
    <span class="hljs-comment">// ....</span>

    $expectedNotification = <span class="hljs-keyword">new</span> PushAccountCreatedNotification(...);

    <span class="hljs-built_in">self</span>::assertSame($expectedNotification, <span class="hljs-built_in">self</span>::getLastDispatchedNotification());
}
</code></pre>
<h2 id="heading-dont-test-language-features">Don’t Test Language Features</h2>
<p>I’ll keep this section as simple as the title describes it already. You don’t need to test the language features. Let’s say you have the following method:</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateUser</span>(<span class="hljs-params"></span>): <span class="hljs-title">Response</span>
</span>{
  <span class="hljs-comment">// ...</span>
}
</code></pre>
<p>You don’t need to validate the return type:</p>
<pre><code class="lang-php"><span class="hljs-comment">#[Test]</span>
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">it_updates_user</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span>
</span>{
  <span class="hljs-comment">// ...</span>
  <span class="hljs-keyword">$this</span>-&gt;assertInstanceOf(Response::class, $updateUserRequest-&gt;send());
}
</code></pre>
<p>This assertion ultimately doesn’t validate anything. I can return a <code>Response</code> from this method while changing its interval behavior completely. This test won’t tell me I’m doing something wrong. Therefore, it doesn’t make sense to have a test like this. Additionally, the language would already warn me if this method doesn’t return the expected type. We don’t need to re-validate language features. Finally, when you validate the expected outcome, that type is already validated implicitly, without you asserting against it. Just test against the expected outcome:</p>
<pre><code class="lang-php"><span class="hljs-comment">#[Test]</span>
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">it_updates_user</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span>
</span>{
  <span class="hljs-comment">// ...</span>
  <span class="hljs-comment">// Build up the expected "Update User" response</span>
  $expectedResponse = <span class="hljs-keyword">new</span> Response(status:<span class="hljs-number">200</span>, body: [...]);

  <span class="hljs-built_in">self</span>::assertEquals($expectedResponse, $updateUserRequest-&gt;send());
}
</code></pre>
<h2 id="heading-dont-validate-prerequisites">Don’t Validate Prerequisites</h2>
<p>Prerequisites are test arrangements that help us reach the expected outcome. Sometimes, we need a user to add a review. Sometimes, we need a review to send an email. Sometimes, we need a booking to generate a ticket. Imagine you need a database record before executing your task, and you have a fixture that handles the insertion. You don’t have to validate the integrity of that fixture helper. Because our goal is to focus on our use case; Our case should already tell us if there is an underlying problem. Imagine we have a service called <code>UserUpdater</code>. It’s a simple service that updates a database record.</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateUser</span>(<span class="hljs-params">User $user</span>): <span class="hljs-title">void</span>
</span>{
    <span class="hljs-comment">// Some validation logic</span>
    <span class="hljs-keyword">if</span> (strlen($user-&gt;name) &gt; <span class="hljs-number">50</span>) {
      <span class="hljs-comment">// throw validation error</span>
    }

    <span class="hljs-keyword">$this</span>-&gt;updateDatabaseRecord($user);
}
</code></pre>
<p>We need a user in the database to validate this scenario. Sometimes we add <code>assertCount</code> or similar assertions; Just to validate the existence of the needed user, explicitly.</p>
<pre><code class="lang-php"><span class="hljs-comment">#[Test]</span>
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">it_updates_user</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span>
</span>{
    $userUpdater = <span class="hljs-keyword">new</span> UserUpdater(...);

    $user = <span class="hljs-built_in">self</span>::createUser(<span class="hljs-string">'Ozan'</span>);
    <span class="hljs-built_in">self</span>::assertCount(<span class="hljs-number">1</span>, <span class="hljs-built_in">self</span>::getUserCountFromDatabase());
    <span class="hljs-built_in">self</span>::assertSame(<span class="hljs-string">'Ozan'</span>, <span class="hljs-built_in">self</span>::getUserFromDatabase($user-&gt;id));

    $user-&gt;setName(<span class="hljs-string">'Akman'</span>);
    $userUpdater-&gt;update($user);

    $expectedUser = User::withId($user-&gt;id, <span class="hljs-string">'Akman'</span>);
    <span class="hljs-built_in">self</span>::assertEquals($expectedUser, <span class="hljs-built_in">self</span>::getUserFromDatabase($user-&gt;id));
}
</code></pre>
<p>The first problem with this approach is we are testing against different concepts. We’re testing against the user creation and the user update. Another issue is that it’s hiding a design flaw. If we want to ensure the existence of this user before the update, if that’s a requirement, then it shouldn’t happen in the test case. It should happen where other business requirements live: the use case itself. The reason we write these kinds of assertions is we’re not very confident with the use case itself. It’s usually pretty easy to shift that mindset from validation in a test case to validation in a use case:</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateUser</span>(<span class="hljs-params">User $user</span>): <span class="hljs-title">void</span>
</span>{
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">$this</span>-&gt;userRepository-&gt;get($user-&gt;id) === <span class="hljs-literal">null</span>) {
        <span class="hljs-keyword">throw</span> UserNotFoundException();
    }

    <span class="hljs-comment">// Other update user code</span>
}
</code></pre>
<p>We want to ensure the update process works as expected. We don’t need to validate the user creation part. This will even make our test case simpler. With the change we did above, if there is an underlying problem with the user creation, our test case will fail. We don't need to assert against it, explicitly.</p>
<pre><code class="lang-php"><span class="hljs-comment">#[Test]</span>
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">it_updates_user</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span>
</span>{
    $userUpdater = <span class="hljs-keyword">new</span> UserUpdater(...);
    $user = <span class="hljs-built_in">self</span>::createUser(<span class="hljs-string">'Ozan'</span>);
    $user-&gt;setName(<span class="hljs-string">'Akman'</span>);

    <span class="hljs-comment">// This call will now throw an exception if there is no user in the database</span>
    $userUpdater-&gt;update($user);

    $expectedUser = User::withId($user-&gt;id, <span class="hljs-string">'Akman'</span>);
    <span class="hljs-built_in">self</span>::assertEquals($user, <span class="hljs-built_in">self</span>::getUserFromDatabase($user-&gt;id));
}
</code></pre>
<h2 id="heading-create-a-test-case-for-each-requirement-change">Create a Test Case for Each Requirement Change</h2>
<p>Business requirements change from time to time. Imagine we have an e-commerce business. Our merchant profiles have a tab called Reviews. The initial requirement was simply to sort reviews by their creation date in descending order. We created a test case to validate this behavior. Great! Some years later, our product expanded into different markets. We started to operate in another country. The legal requirement in a new country is the updated reviews have to be on the top of the Reviews list. We implemented a simple Enum called <code>ReviewOrder</code> to tweak this setting. In this case, the default behavior remained the same. We still retrieve the reviews by their creation date in descending order. We can keep the initial test as it is, but we should provide another test case, that is specifically addressing this new behavior. If we don’t create this test case, tomorrow someone else will change this behavior and break the system, causing a legal issue, without even noticing it.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Congratulations if you made it here! We’ve learned important practices about automated testing! I hope they will help you create better test cases and add more confidence to your workflow. It’s important to remember these tips are there to help you. They’re not immutable laws. Sometimes it’s okay to bend these rules if there is a better fit for a specific case.</p>
<p>I hope to publish regularly about software and personal development topics. I’m trying to simplify complex technical concepts and provide a simpler language with a lot of examples. Feel free to share your thoughts in the comments!</p>
<p>If you enjoyed this article, let’s connect on <a target="_blank" href="https://twitter.com/akmandev">https://twitter.com/akmandev</a> and <a target="_blank" href="https://www.linkedin.com/in/ozanakman">https://www.linkedin.com/in/ozanakman</a> for more content like this!</p>
]]></content:encoded></item><item><title><![CDATA[Soft Skills in Software Engineering]]></title><description><![CDATA[Introduction
If you’re running a business, “We need to refactor this module.” is not something you’d like to hear. It doesn’t mean anything unless you come from a technical origin. In fact, it’s a scary statement because that means you need to change...]]></description><link>https://compiler.blog/soft-skills-in-software-engineering</link><guid isPermaLink="true">https://compiler.blog/soft-skills-in-software-engineering</guid><category><![CDATA[Beginner Developers]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[Soft Skills]]></category><dc:creator><![CDATA[Ozan Akman]]></dc:creator><pubDate>Sat, 17 Feb 2024 17:46:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/V5vqWC9gyEU/upload/ee646e7db6f817486957ed96a70bc1ab.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>If you’re running a business, “We need to refactor this module.” is not something you’d like to hear. It doesn’t mean anything unless you come from a technical origin. In fact, it’s a scary statement because that means you need to change something that was somehow working before. As Kent Beck explains in his book, “Tidy First?”: “No new features, possible damage, and nothing to show for it at the end. No, thank you”.</p>
<p>Managers understand the problem space, the domain, and how to manage an organization, not technical implementation details. They aren’t concerned whether you use Next.js, React, Symfony, or Laravel with Livewire. This is also a valid statement for your users. They don’t care about technical implementations; They care about the service.</p>
<p>Managers are responsible for acquiring a market share in a specific domain. They prioritize a product that works reliably and addresses end users’ needs. However, the problem arises when engineers become disconnected from this reality of business world. We expect managers to comprehend our technical point of view and even feel disappointed when they don’t. We want them to understand the significance of “We need to refactor this”. Because it’s really important for us. If you have a manager coming from an engineering background, that is great! Today, we’re going to talk about the other half, where managers and engineers are disconnected.</p>
<h2 id="heading-why-dont-managers-understand-software-engineers">Why Don’t Managers Understand Software Engineers?</h2>
<p>Because we don’t really know how to explain ourselves. We’re stuck with technical terminology. We’re saying stuff like “refactoring” or “technical debt” that doesn’t make sense to most people. They don’t understand about the trade-offs. If a task can be done fast, we should finish it fast. We should stop saying, “Yeah, I could try to do this in a week, but it will be really hard”. This translates into you’re not trying very hard if you’re not delivering it within a week. So you just created a problem for yourself. We often fail to communicate effectively because we don’t think much about how we sound. We don’t try to understand how business operates. Because we’re stuck with technical details. We don’t have time to think about our users, our business, and their needs. We can’t come up with non-technical solutions. We don’t look professional.</p>
<p>I want to make it clear that this is not an attack on engineers. In fact, I used to be in the same position earlier in my career. This is simply an honest self-criticism. I was frustrated by the fact that nobody seemed to understand the importance of refactoring.</p>
<h2 id="heading-make-it-important-its-more-than-just-bad-code">Make It Important: It’s More Than Just Bad Code</h2>
<p>We need to explain that if we invest some resources in refactoring the problematic module, it would free up mental space for us to focus on other areas of our product. However, this shouldn’t be only about technical reasons. We should provide other rational reasons that make sense to everybody, not just developers.</p>
<p>It’s hard to maintain is not an argument. Your job is to maintain it. It doesn’t matter if it’s hard or not. It’s hard for you, not for the other side of the business. It only matters when you make it important. Many engineers struggle to understand this part. They know the problematic module hurts the business. However, they don’t know how to communicate this issue in non-technical language. We need to find a common language that everybody can understand. Why don’t we present it with actual numbers? Something like this:</p>
<blockquote>
<p>We have to refactor the booking module because it’s not only hard to maintain, it caused 11 moderate bugs, and 2 severe bugs last year. The severe bugs cost over $47,500, while the moderate bugs cost around $43,000. We received a total of 197 reports from our users, and one of the bugs even caused a severe security issue that could lead to legal consequences. We have a concrete plan to refactor this area over the next two months. We’ll create a workforce of 3 developers, each focusing on different areas. You can find the plan and detailed list below. These two months of refactoring will save us approximately $65,000 this year, and almost the same amount next year. This will not only solve current bugs, but also improve User Experience (UX), and help us solve future issues quickly. At the moment, we’re spending a lot of time on even small issues. The booking module is currently like a maze, making it hard for everyone. We could put that effort into developing new features.</p>
</blockquote>
<p>It feels different from saying “We need to refactor this”, doesn’t it? Sure, it takes a bit more effort than writing one sentence, but if you add up all the complaints, they probably take even more time. Don’t say it’s hard to maintain it. Explain how it hurts your business and User Experience (UX). Try to put yourself in the shoes of the business and the user. Explain how everybody could benefit from this change. It can be difficult for managers to understand why a profitable business should spend time fixing something that makes money.</p>
<h2 id="heading-complaining-vs-problem-solving">Complaining vs. Problem-Solving</h2>
<p>Soft skills are incredibly valuable to make a great career and connections. Nobody can understand you and your problems if you can’t explain yourself. It’s your responsibility to make sure others understand you, not theirs. This statement is also true for your personal life. Do you genuinely care about resolving the issue, or do you want to complain? Complaining might feel convenient, but it only provides temporary relief without addressing the actual problem. I’ve had colleagues who complained about everything the moment they walked into the office every day. They wanted everyone to understand their issues, but they never tried to understand anyone else’s. To be honest; I don’t miss working with negative people who complain all the time, without attempting to solve any problems. They only spread negativity. You shouldn’t be that person. Complaining doesn’t solve any problems, including yours.</p>
<p>Some people tend to complain, but they also try to find solutions and understand the situation better. It’s totally normal to feel overwhelmed and sometimes complain, especially when you care about something and want to improve it. What I’m saying is, you should remain logical and objective while expressing your frustration. Maybe the problems you’re facing aren’t only your problems, and you don’t have to solve them on your own. It’s perfectly fine to ask for help. But keep in mind that just complaining without taking any action doesn’t help anybody.</p>
<h2 id="heading-communication">Communication</h2>
<p>We use computers and code to solve our problems, but we don’t solve computer problems. Ultimately, we are solving human problems. Computers are not our clients. There will be always communication and connection. If we disconnect from our clients, team, and managers, we won’t understand the problems we need to solve. We should focus on the actual problem and avoid drowning down in technical details. That’s why understanding and practicing both <a target="_blank" href="https://compiler.blog/series/domain-driven-design">Domain-Driven Design (DDD)</a> and <a target="_blank" href="https://compiler.blog/understanding-automated-testing">Test-Driven Development (TDD)</a> is helpful. We become more connected to the actual problem rather than focusing solely on implementation details. Effective problem-solving requires communication. That’s what companies, managers, and other people are looking for, they’re looking for problem-solvers who can communicate.</p>
<h2 id="heading-ability-to-express-ideas">Ability to Express Ideas</h2>
<p>You’re what you can express to the world. People can only understand you as much as you can explain yourself. Many of us had times when we knew something but couldn’t put it into words. That even made us question if we knew that thing. It usually happens because we have trouble articulating our thoughts. Sometimes, it’s just a lack of understanding or vocabulary. Sometimes, the other person doesn’t have a technical background. Sometimes, we don’t know how to simplify it. We don’t know how to abstract the concept. You need to organize your ideas and make them clear. They need to be result-oriented. If you’re having trouble explaining what you know and what’s causing you trouble, maybe it’s time to start reading and writing more. You don’t have to share it. Writing is a great and relaxing practice. Explaining yourself on a white paper will eventually help explain yourself to people. You will understand how you sound and find ways to make it better. It allows you to brainstorm with yourself.</p>
<h2 id="heading-constructive-self-criticism">Constructive Self-criticism</h2>
<p>It’s hard to accept that our personal and professional lives are affected by many aspects of ourselves. It’s hard to accept that impacts beyond us, our teammates, or partners. No one ever teaches us this skill, and it’s hard to comprehend. Sometimes, we constantly judge ourselves, which can become an unhealthy habit. But generally, I think constructive self-criticism is an effective tool. I focus on behaviors that I can change rather than global and unchangeable problems. Also, I don’t think about the feelings but prioritize outcomes. These outcomes don’t only influence me, but other people as well. You don’t need to be pushy on yourself but understand situations from other people’s perspective. Constructive self-criticism gives you space and understanding. It’s a tool that helps us form feedback about ourselves.</p>
<p>It’s possible that the functionality you spent hours on might not add any value to the product. Your code reviews may be just holding other developers back rather than providing constructive feedback. If you’re grumpy all the time, it may make people not want to work with you. If you always push people without empathy, you might not be the best leader. It’s about understanding and having empathy.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Of course, external factors can affect our work. Sometimes, a manager has no skills to manage a team, or they’re difficult to work with. Other times, they may lack empathy or micromanage, making it hard for the team to function. I totally understand all those situations. They’re common problems. This article is not about them. Instead, it’s about your personal growth. If you want to build the best version of yourself, you shouldn’t limit yourself to technical skills only. You should develop communication and problem-solving abilities. Don’t let external circumstances limit your potential.</p>
<h2 id="heading-stay-connected">Stay Connected</h2>
<p>Feel free to share your thoughts in the comments! I hope to publish every week on various software-related topics.</p>
<p>If you enjoyed this article, let’s connect on <a target="_blank" href="https://twitter.com/akmandev">https://twitter.com/akmandev</a> and <a target="_blank" href="https://www.linkedin.com/in/ozanakman">https://www.linkedin.com/in/ozanakman</a> for more content like this!</p>
]]></content:encoded></item><item><title><![CDATA[Understanding Automated Testing]]></title><description><![CDATA[Introduction
Hello there! Today’s talk is about a complicated topic that is often misunderstood by mid to senior developers. We’re going to talk about Automated Testing. I’ve struggled to understand its value and purpose, for almost my entire career....]]></description><link>https://compiler.blog/understanding-automated-testing</link><guid isPermaLink="true">https://compiler.blog/understanding-automated-testing</guid><category><![CDATA[Programming Blogs]]></category><category><![CDATA[software development]]></category><category><![CDATA[Beginner Developers]]></category><category><![CDATA[Automated Testing]]></category><dc:creator><![CDATA[Ozan Akman]]></dc:creator><pubDate>Mon, 05 Feb 2024 08:22:59 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/8bghKxNU1j0/upload/d01a828aadc80efb918c6a0b38173c92.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>Hello there! Today’s talk is about a complicated topic that is often misunderstood by mid to senior developers. We’re going to talk about Automated Testing. I’ve struggled to understand its value and purpose, for almost my entire career. If you feel like you’re lost when it comes to testing, don’t worry, you’re not alone. Of course, these topics are complex by their nature, and it’s not easy to make sense of them, immediately. But I don’t think that’s the main issue. The actual problem comes from learning from people who don’t understand it. The best available testing resources are very technical and hard to digest. Therefore, companies and developers apply automated testing without understanding it. They’re stuck with technical terms and bad examples. Well, it doesn’t have to be that complicated if done right.</p>
<p>In our “<a target="_blank" href="https://compiler.blog/series/domain-driven-design">Understand Domain-Driven Design</a>” series, I briefly explained the importance of behavior testing. In this series, we’ll delve deeper into descriptions, examples, code illustrations, and best practices. We’re going to question what we know about testing and understand more.</p>
<p>Shout out to my colleague Sebastien Garcia, for his efforts in teaching automated testing. He inspired me to write this article!</p>
<h2 id="heading-what-is-automated-testing">What is Automated Testing?</h2>
<p>Automated testing is proof of your software’s expected behavior. It’s a documentation where you explain your instructions leads to an expected outcome. Tests can either pass or fail and if a test case fails, it means that your code is not producing the expected result. Properly designed test cases can help pinpoint the underlying issues. On the contrary, poorly designed test cases can hide potential issues.</p>
<p>Can’t we just test our software manually? Yes, we can and manual testing has its place, such as User Acceptance Testing (UAT) or User Interface (UI) testing. We ensure everything looks and functions fine. But this type of testing is time-consuming and expensive. It’s also almost impossible to ensure the stability of the system being tested. It stays a bit on the surface. Especially in large enterprise projects with dozens of use cases if not hundreds. The use cases are complicated and they require many different steps. You might argue “Well, test cases are created by developers who can make mistakes”. Yes, they can. Our goal is to understand why those mistakes happen and how to reduce their frequency. To do this, we need to answer some fundamental questions about testing.</p>
<h2 id="heading-whats-the-purpose-of-testing">What’s The Purpose Of Testing?</h2>
<p>As I explained in the <a target="_blank" href="https://compiler.blog/series/domain-driven-design">Understanding DDD</a> series, programming is about solving real-world problems. That’s why we focus on Use Cases rather than technical terms. We want to get feedback fast when we’re implementing those use cases. Getting that feedback from users is pretty expensive and mostly, a terrible User Experience (UX). Imagine you go to a gym, and the gym equipment works only half the time or sometimes breaks while you’re using it, or it’s already broken. Would you want to go to that gym again? The same goes for the software products. If your product fails to meet expectations, your users will be frustrated and look for alternatives. We want to avoid that. We want to get that feedback as early as in the development stage. Think about times when your work was stressful. The stress usually comes from uncertainty and insecurity about your or your company’s development approach. The purpose of automated testing is to eliminate that uncertainty and insecurity. We want to solve our problems confidently. We also want to bring better UX to the table.</p>
<h2 id="heading-no-value-equals-to-no-tests">No Value Equals To No Tests</h2>
<p>Early in my career, I couldn’t quite grasp the idea of automated testing. I joined a company where people practiced automated testing. I was pretty happy to learn. I quickly started to read the existing tests. There were tests for basic ValueObjects (VO) without behavior, DataTransferObjects (DTO), or configurations. Tests with many mocks and assertions only validated the mocks’ output that we previously configured. There were a lot of assertions about every possible detail. If we were testing a database query, we wouldn’t only assert against the result but also against the underlying SQL string and the row count. Testing felt like a burden. It didn’t add any confidence, and I didn’t feel great writing them. I mostly didn’t even read testing-related code when I reviewed merge/pull requests. Because it was insanely complicated, every time. I didn’t understand why we created those cases, but of course, I followed them and created similar cases. I thought that was testing.</p>
<p>The truth is nobody knew what they were doing. This type of testing doesn’t bring any value for developers or users, therefore, no value for the business. You don’t need to write tests for every little detail. It’s redundant sometimes. You don’t need to write a unit test for a DTO that will be already validated when you validate the behavior of the higher-level components. Also, even for higher-level components, you don’t need to test against the implementation details such as SQL string. That’s not really important. This detail will be already validated when you test against the expected outcome. The result is important. It should produce what we would expect when we run that query. If your SQL string is wrong in the first place, you won’t get the desired outcome anyway. You don’t need to assert specifically against the SQL string.</p>
<h3 id="heading-dont-overlook-small-functions">Don’t Overlook Small Functions</h3>
<p>There is something else about testing value. Sometimes it feels like the tests don’t add value when the functionality is pretty small. That’s not always the case. The real value of testing lies in catching potential issues before they make their way into production and are discovered by users. Although, complicated solutions indeed solve complicated problems, and their tests bring more value. But even a small function can benefit from testing. Let’s imagine the following JavaScript function:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sum</span>(<span class="hljs-params">a, b</span>) </span>{
   <span class="hljs-keyword">return</span> a + b;
}
</code></pre>
<p>I love this example because it’s so simple: the function is supposed to add two numbers. It looks like we don’t really need to validate its behavior since it’s obvious, right? Well, it’s not the case. If you input a string instead of a number, the result will be completely different from entering two numbers. Even though the function is tiny, there can be hidden bugs.</p>
<h2 id="heading-whats-test-driven-development-tdd">What’s Test-Driven Development (TDD)?</h2>
<p>Test-Driven Development (TDD) is a programming technique. It helps us create a small feedback window. It allows us to verify if our scenarios lead to the expected outcomes at the earliest stage. It’s not only about writing the tests first or covering every line with tests. It’s about getting fast feedback on your design. That’s why TDD is both a testing and design approach. Initially, you define a list of scenarios without implementation details. You build those scenarios one after the other and anticipate possible problems with your design, then refactor your design until the test case passes. Repeat the steps until you cover all the scenarios–cases. This approach is often misunderstood, even by experienced folks. TDD is about understanding what your system is supposed to do and validating its behavior in tiny steps. It’s a specification technique where you verify all your assumptions.</p>
<h2 id="heading-whats-arrange-act-assert-aaa-approach">What’s Arrange-Act-Assert (AAA) Approach?</h2>
<p>The Arrange-Act-Assert (AAA) or 3A is a testing technique that focuses on isolating the test’s state and expected outcome. Each “A” represents a different step. It’s similar to the Given-When-Then approach.</p>
<ul>
<li><p>Arrange refers to the initial setup of your case. It could be something like “I need a repository to save a post” or “I need an author to create a post”. Essentially, it’s about the requirements to execute the task.</p>
</li>
<li><p>Act involves an action or set of actions, such as login as a user and dispatching a command.</p>
</li>
<li><p>Assert means comparing the expected outcome to the actual result.</p>
</li>
</ul>
<p>I prefer the AAA approach because it helps me focus on the behavior rather than implementations. Therefore, reduces the implementation coupling in testing. It also helps formulate the test cases easily.</p>
<h2 id="heading-types-of-software-testing">Types of Software Testing</h2>
<p>Software systems are complex structures. There are different environments, layers, and building blocks. In an ideal world, we would have unit tests for every building block to validate their behavior, ensuring that all units are verified individually and work when they’re combined. However, that’s not the reality. The reality is we use open-source libraries and configurations from other sources. Even though you might have a perfect unit testing suite, it’s hard to ensure the integration of different components. Imagine you’re using a database or framework. How can you ensure that integration works as intended by only unit testing your blocks? You can’t. Therefore, you need a different type of validation–a testing type where you can boot up the framework configuration and test that configuration altogether. Sometimes, you want to test your system from a specific protocol and need a client to perform that test.</p>
<p>Testing types are mainly categorized as White-box testing, Black-box testing, and Gray-box testing.</p>
<h3 id="heading-white-box-testing">White-box Testing</h3>
<p>White-box testing is mainly concerned with single-unit. The tester has access to the internal design. This type of testing focuses on individual components to ensure they function correctly in isolation. It’s like validating individual computer hardware without assembling the whole computer.</p>
<h3 id="heading-black-box-testing">Black-box Testing</h3>
<p>Black-box testers only have access to the inputs and outputs of the application, not to the internal design. This type of testing ensures that the application behaves as expected for the end user and meets its requirements. Functional testing and end-to-end testing are some of the most common types of Black-box testing.</p>
<h3 id="heading-gray-box-testing">Gray-box Testing</h3>
<p>Gray-box testing combines both White-box testing and Black-box testing. It partially knows about the internal design. It validates the outcome using that knowledge. One of the most known forms of Gray-box testing is Integration testing. For example, a tester might have access to the application container and focus on the interaction between the dispatched command and other components. Another example is when a tester knows about the database and focuses on specific data interactions between different parts.</p>
<h3 id="heading-differences-between-testing-types">Differences Between Testing Types</h3>
<p>All types of testing are validating the behavior. They validate it from different perspectives. The differences come from how we build our test cases and what we want to validate about our design. All tests should be isolated and use shared fixtures only for static data like postal codes, countries, currencies, device types (e.g., desktop, tablet, mobile), file types, user roles (e.g., admin, editor, viewer), etc. Be cautious about overusing shared fixtures, as it might lead to unintended dependencies between tests.</p>
<p>Let’s create a functional test case to understand what Black-box testing means. We’ll also see examples from other types. The examples will be simplified versions for demo purposes.</p>
<h4 id="heading-black-box-testing-functional-test-case">Black-box Testing: Functional Test Case</h4>
<pre><code class="lang-php"><span class="hljs-comment">#[Test]  </span>
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">it_books_flight_ticket</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span>  
</span>{  
    <span class="hljs-comment">// Arrange  </span>
    $departureDate = <span class="hljs-string">'2024-02-17'</span>;  
    $returnDate = <span class="hljs-string">'2024-02-17'</span>;  
    $origin = [<span class="hljs-string">'airportId'</span> =&gt; <span class="hljs-string">'BER'</span>, <span class="hljs-string">'city'</span> =&gt; <span class="hljs-string">'Berlin'</span>];  
    $destination = [<span class="hljs-string">'airportId'</span> =&gt; <span class="hljs-string">'ADB'</span>, <span class="hljs-string">'city'</span> =&gt; <span class="hljs-string">'İzmir'</span>];  

    <span class="hljs-built_in">self</span>::createAvailableFlightsBetween(
        $departureDate,
        $returnDate,
        $origin,
        $destination,
    );  

    $client = <span class="hljs-keyword">new</span> HttpClient();  
    $request = <span class="hljs-keyword">new</span> Request(
        url: <span class="hljs-built_in">self</span>::BOOK_FLIGHT_ENDPOINT,  
        method: RequestMethod::POST,  
        body: [  
            <span class="hljs-string">'origin'</span> =&gt; $origin,  
            <span class="hljs-string">'destination'</span> =&gt; $destination,  
            <span class="hljs-string">'depart'</span> =&gt; $departureDate,  
            <span class="hljs-string">'return'</span> =&gt; $returnDate,  
        ],  
    );  

    <span class="hljs-comment">// Act  </span>
    <span class="hljs-built_in">self</span>::loginAsCustomer();
    $actualResponse = $client-&gt;send($request);  

    <span class="hljs-comment">// Assert  </span>
    $expectedResponse = <span class="hljs-keyword">new</span> Response(  
        [<span class="hljs-string">'message'</span> =&gt; <span class="hljs-string">'BOOKING_SUCCESS'</span>, <span class="hljs-string">'context'</span> =&gt; [...]],  
        <span class="hljs-number">200</span>,  
    );  
    <span class="hljs-built_in">self</span>::assertEquals($expectedResponse, $actualResponse);  
}
</code></pre>
<p>In this example above, we don’t know how the internals work. We act as a client, sending an input and receiving output.</p>
<h4 id="heading-gray-box-testing-integration-test-case">Gray-box Testing: Integration Test Case</h4>
<p>On the other hand, Gray-box testers know about the internal design. For example, they can access the application container. This type of testing ensures that the application is functioning at the code level and identifies any bugs or issues that may not be visible from the client’s perspective. Imagine a scenario where you want to ensure a specific notification is triggered when a new account is created.</p>
<pre><code class="lang-php"><span class="hljs-comment">#[Test]  </span>
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">it_sends_new_account_notification_on_account_creation</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span>  
</span>{  
    <span class="hljs-comment">// Arrange</span>
    $role = <span class="hljs-built_in">self</span>::sharedFixtures()-&gt;defaultRole();

    $command = <span class="hljs-keyword">new</span> CreateAccount(  
        username: <span class="hljs-string">'SomeCoolUsername'</span>,  
        password: <span class="hljs-string">'Som#3Pa$$%word'</span>,
        role: $role,  
    );

    <span class="hljs-comment">// Decorate the NotificationDispatcher, so we can trace the dispatched notifications.  </span>
    $spyNotificationDispatcher = <span class="hljs-keyword">new</span> TraceableNotificationDispatcher(  
        <span class="hljs-built_in">self</span>::getService(NotificationDispatcher::class),  
    );  
    <span class="hljs-comment">// Replace the actual service with the decorated one, in the application container.</span>
    <span class="hljs-built_in">self</span>::setService(NotificationDispatcher::class, $spyNotificationDispatcher);  

    <span class="hljs-comment">// Act</span>
    <span class="hljs-built_in">self</span>::dispatchCommand($command);

    <span class="hljs-comment">// Assert  </span>
    $expectedNotification = <span class="hljs-keyword">new</span> AccountCreatedNotification(username: <span class="hljs-string">'SomeCoolUsername'</span>);
    <span class="hljs-built_in">self</span>::assertTrue($spyNotificationDispatcher-&gt;hasNotificationBeenDispatched($expectedNotification));  
}
</code></pre>
<p>Here, we have access to the application container. Instead of validating the created account, we focus on the secondary behavior, the correct notification dispatch through integration.</p>
<h4 id="heading-white-box-testing-unit-test-case">White-box Testing: Unit Test Case</h4>
<p>This type of testing will focus on single-unit behavior. We provide test doubles, such as <code>Stub</code>, <code>Spy</code>, or <code>InMemoryRepository</code>, when we rely on other dependencies. Because we don’t focus on integrations in this type of testing. It helps us verify the behavior of the given unit in isolation. We don’t need a container, framework, or anything else that you would need to run an application. That’s why unit testers are pretty fast.</p>
<pre><code class="lang-php"><span class="hljs-comment">#[Test]  </span>
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">it_creates_post</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span>  
</span>{  
    <span class="hljs-comment">// Arrange</span>
    $postRepository = <span class="hljs-keyword">new</span> InMemoryPostRepository();  

    $time = <span class="hljs-keyword">new</span> DateTimeImmutable();  
    $frozenClock = <span class="hljs-keyword">new</span> StubClock($frozenTime);

    $author = Author::create(  
        email: <span class="hljs-string">'test@example.com'</span>,  
        name: <span class="hljs-string">'Test User'</span>,  
    );

    $useCase = <span class="hljs-keyword">new</span> CreatePost(  
        $postRepository,  
        <span class="hljs-keyword">new</span> SpyEventDispatcher(),  
        $frozenClock,
    );  

    <span class="hljs-comment">// Act  </span>
    $useCase-&gt;create($author, <span class="hljs-string">'Post Title'</span>);  

    <span class="hljs-comment">// Assert</span>
    $actualPost = $postRepository-&gt;getLastCreatedPost();  

    $expectedPost = Post::createWithId(  
        id: $actualPost-&gt;id,  
        author: $author,  
        title: <span class="hljs-string">'Testing Title'</span>,  
        createdAt: $time,  
    );  

    <span class="hljs-built_in">self</span>::assertEquals($expectedPost, $actualPost);  
}
</code></pre>
<h3 id="heading-what-would-be-considered-a-bad-test">What Would Be Considered a Bad Test?</h3>
<p>Imagine, you have a console command that converts CSV files into database entries.</p>
<pre><code class="lang-php"><span class="hljs-comment">#[AsCommand(name: 'app:migrate-users')]  </span>
<span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">StoreCsvFileCommand</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Command</span>  
</span>{  
    <span class="hljs-keyword">protected</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">execute</span>(<span class="hljs-params">InputInterface $input, OutputInterface $output</span>): <span class="hljs-title">int</span>  
    </span>{
        $entry = <span class="hljs-keyword">$this</span>-&gt;csvToDbConverter-&gt;convert($input-&gt;getArgument(<span class="hljs-string">'csvPath'</span>));
        <span class="hljs-keyword">$this</span>-&gt;bookingRepository-&gt;save($entry);

        $output-&gt;writeln(<span class="hljs-string">'CSV content successfully converted!'</span>);

        <span class="hljs-keyword">return</span> Command::SUCCESS;
    } 
}
</code></pre>
<p>Typically, if you don’t understand the behavior’s importance, the test becomes an implementation test.</p>
<pre><code class="lang-php"><span class="hljs-comment">#[Test]</span>
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_execute</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span> ❌ (<span class="hljs-params">A</span>)
</span>{
    $applicationContainer = <span class="hljs-comment">// ...;  </span>
    $command = $applicationContainer-&gt;get(StoreCsvFileCommand::class); 
    $commandTester = <span class="hljs-keyword">new</span> CommandTester($command);  
    $commandTester-&gt;execute([<span class="hljs-string">'csvPath'</span> =&gt; <span class="hljs-string">'someFile.csv'</span>]);

    <span class="hljs-comment">// A private assertion method to validate command name</span>
    <span class="hljs-built_in">self</span>::assertLastExecutedCommandName(<span class="hljs-string">'app:migrate-users'</span>); ❌ (B)

    $actualOutput = $commandTester-&gt;getDisplay();  
    $expectedOutput = <span class="hljs-string">'CSV content successfully converted!'</span>;  
    <span class="hljs-built_in">self</span>::assertSame($expectedOutput, $actualOutput); ❌ (C)
}
</code></pre>
<p>Let’s review this test case together. I’ve already marked some problems with the ❌:</p>
<ul>
<li><p>A: The name doesn’t provide any information about the use case and its behavior.</p>
</li>
<li><p>B: We are already retrieving the command by its class name. We don’t care about its configuration name. The configuration name doesn’t validate anything about the command’s behavior.</p>
</li>
<li><p>C: The output text of the command is irrelevant when it comes to its behavior. The behavior is to store CSV entries in the database, not to output a particular text.</p>
</li>
</ul>
<p>How could we change this test case?</p>
<pre><code class="lang-php"><span class="hljs-comment">#[Test]</span>
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">it_converts_csv_file_into_database_entries</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span> ✅ (<span class="hljs-params">A</span>)
</span>{
    $applicationContainer = <span class="hljs-comment">// ...;  </span>
    $command = $applicationContainer-&gt;get(StoreCsvFileCommand::class);
    $commandTester = <span class="hljs-keyword">new</span> CommandTester($command);  
    $commandTester-&gt;execute([<span class="hljs-string">'csvPath'</span> =&gt; <span class="hljs-string">'someFile.csv'</span>]); 

    $expectedEntries = [...]; <span class="hljs-comment">// Array of expected entries</span>
    $actualEntries = $applicationContainer-&gt;get(ImportedBookingRepository::class)-&gt;findAll();
    <span class="hljs-built_in">self</span>::assertEquals($expectedEntries, $actualEntries); ✅ (C)
}
</code></pre>
<ul>
<li><p>A: We added a name that describes what the use case is about.</p>
</li>
<li><p>B: The command name is not that important. It doesn’t impact the behavior; it’s a configuration detail. Once you validate this command’s behavior, you already know you’re testing the correct command, implicitly confirming the command name.</p>
</li>
<li><p>C: We asserted against the expected outcome, not the console output. Once again, we can change this output message at any time without affecting the use case itself. It’s an implementation detail.</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>I’ll list some of the principles of effective testing, I regularly use. I follow them with rare exceptions.</p>
<ul>
<li><p>Test behavior, not implementation details.</p>
</li>
<li><p>Test abstractions, not implementations.</p>
</li>
<li><p>Test public API, what a unit exposes to outside.</p>
</li>
<li><p>Don’t test private methods, they’re implementation details.</p>
</li>
<li><p>Isolate all tests with some shared fixtures such as postal codes, countries, and currencies.</p>
</li>
</ul>
<p>Great job finishing the article! I know it’s a lot of information to digest, but it gets easier once you start applying it. We’ve learned about Automated Testing, the purpose of testing, Test-Driven Development (TDD), and the importance of behavior testing. Feel free to share your thoughts in the comments! I hope to publish every week on various software-related topics. I’m pretty sure I’ll share a lot more about testing!</p>
<p>If you enjoyed this article, let’s connect on <a target="_blank" href="https://twitter.com/akmandev">https://twitter.com/akmandev</a> and <a target="_blank" href="https://www.linkedin.com/in/ozanakman">https://www.linkedin.com/in/ozanakman</a> for more content like this!</p>
]]></content:encoded></item><item><title><![CDATA[Understanding Domain-Driven Design (Part 3)]]></title><description><![CDATA[Introduction
Hello there! We’re back with our “Understanding Domain-Driven Design” series. So far, we’ve talked about Domain, Bounded Context, Use Case, file structure, and testing, in Parts 1 & 2. I would suggest you read those first if you haven’t ...]]></description><link>https://compiler.blog/understanding-domain-driven-design-part-3</link><guid isPermaLink="true">https://compiler.blog/understanding-domain-driven-design-part-3</guid><category><![CDATA[Programming Blogs]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[software development]]></category><category><![CDATA[Software Engineering]]></category><dc:creator><![CDATA[Ozan Akman]]></dc:creator><pubDate>Mon, 29 Jan 2024 06:50:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/ahi73ZN5P0Y/upload/63e664e91ff1f9b3e34abf59fe9e673e.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>Hello there! We’re back with our “Understanding Domain-Driven Design” series. So far, we’ve talked about Domain, Bounded Context, Use Case, file structure, and testing, in Parts 1 &amp; 2. <a target="_blank" href="https://compiler.blog/series/domain-driven-design">I would suggest you read those first if you haven’t done it, yet.</a></p>
<p>In this part, we will go through abstracting smaller parts. We will also talk about avoiding unnecessary complexity.</p>
<h2 id="heading-the-big-picture">The Big Picture</h2>
<p>The Use Cases are essentially a set of instructions to our system. They’re the big picture. That’s why, most of the time, testing a use case’s behavior already reveals underlying problems without delving deeper. But, there are some cases where the big picture is not the full picture. Sometimes, we need a little more detail. Let’s think about the “buying a flight ticket” process from <a target="_blank" href="https://compiler.blog/understanding-domain-driven-design-part-2#heading-use-case-driven-approach">the previous part.</a></p>
<h3 id="heading-abstracting-use-cases-into-smaller-building-blocks">Abstracting Use Cases Into Smaller Building Blocks</h3>
<p>In traditional apps, you might find all use cases handled by a single controller, such as <code>BookingController</code>. This controller would manage the entire process of buying a flight ticket. Checking availability, calculating prices, making reservations, processing payments, generating tickets, sending notifications, and the list continues. When the complexity of this controller becomes hard to manage, we look for ways to split it into smaller units.</p>
<p>Ultimately, we should anticipate and address those potential problems before they become problems, even in DDD apps. We have to be careful, though: we don’t want to overcomplicate our solutions while trying to simplify them. This distinction is not an easy challenge. It is hard. You need experience to do that properly. I will give you a little secret, though. When you focus on the behavior of the system rather than implementation details, it gets easier. What does that mean? And how do we focus on the behavior? We create a test case explaining the desired behavior. I explained this briefly in the previous article. We can leverage Test-Driven Development (TDD) to design our system rather than testing its implementation.</p>
<h4 id="heading-testing-the-behavior">Testing The Behavior</h4>
<p>Imagine, you have a Symfony console command that converts CSV file into database entries.</p>
<pre><code class="lang-php"><span class="hljs-comment">#[AsCommand(name: 'app:booking-importer:store-csv-file')]  </span>
<span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">StoreCsvFileCommand</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Command</span>  
</span>{  
    <span class="hljs-comment">// ...</span>
    <span class="hljs-keyword">protected</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">execute</span>(<span class="hljs-params">InputInterface $input, OutputInterface $output</span>): <span class="hljs-title">int</span>  
    </span>{
        $entry = <span class="hljs-keyword">$this</span>-&gt;csvToDbConverter-&gt;convert($input-&gt;getArgument(<span class="hljs-string">'csvPath'</span>));
        <span class="hljs-keyword">$this</span>-&gt;bookingRepository-&gt;save($entry);

        $output-&gt;writeln(<span class="hljs-string">'CSV content successfully converted!'</span>);

        <span class="hljs-keyword">return</span> Command::SUCCESS;
    } 
}
</code></pre>
<p>Let’s create an integration test case following the framework documentation. Typically, if you don’t understand the behavior’s importance, the test becomes an implementation test.</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_execute</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span> ❌ (<span class="hljs-params">A</span>)
</span>{
    $applicationContainer = <span class="hljs-comment">// ...;  </span>
    $command = $applicationContainer-&gt;get(StoreCsvFileCommand::class); 
    $commandTester = <span class="hljs-keyword">new</span> CommandTester($command);  
    $commandTester-&gt;execute([<span class="hljs-string">'csvPath'</span> =&gt; <span class="hljs-string">'someFile.csv'</span>]);

    <span class="hljs-comment">// A private assertion method to validate command name</span>
    <span class="hljs-built_in">self</span>::assertLastExecutedCommandName(<span class="hljs-string">'app:importer:store-csv-file'</span>); ❌ (B)

    $actualOutput = $commandTester-&gt;getDisplay();  
    $expectedOutput = <span class="hljs-string">'CSV content successfully converted!'</span>;  
    <span class="hljs-built_in">self</span>::assertSame($expectedOutput, $actualOutput); ❌ (C)
}
</code></pre>
<p>Let’s review this test case together. I’ve already marked some problems with the ❌:</p>
<ul>
<li><p>A: The name doesn’t provide any information about the use case and its behavior.</p>
</li>
<li><p>B: We are already retrieving the command by its class name. We don’t care about its configuration name. The configuration name doesn’t validate anything about the command’s behavior.</p>
</li>
<li><p>C: The output text of the command is irrelevant when it comes to its behavior. The behavior is to store CSV entries in the database, not to output a particular text. How would I change this test case?</p>
</li>
</ul>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test_it_converts_csv_file_into_database_entries</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span> ✅ (<span class="hljs-params">A</span>)
</span>{
    $applicationContainer = <span class="hljs-comment">// ...;  </span>
    $command = $applicationContainer-&gt;get(StoreCsvFileCommand::class);
    $commandTester = <span class="hljs-keyword">new</span> CommandTester($command);  
    $commandTester-&gt;execute([<span class="hljs-string">'csvPath'</span> =&gt; <span class="hljs-string">'someFile.csv'</span>]); 

    $expectedEntries = [...]; <span class="hljs-comment">// Array of expected entries</span>
    $actualEntries = $applicationContainer-&gt;get(ImportedBookingRepository::class)-&gt;findAll();
    <span class="hljs-built_in">self</span>::assertEquals($expectedEntries, $actualEntries); ✅ (C)
}
</code></pre>
<ul>
<li><p>A: We described the behavior of the use case in the test case name.</p>
</li>
<li><p>B: The command name is not that important. It doesn’t impact the behavior; it’s a configuration detail. Once you validate this command’s behavior, you already know you’re testing the correct command, implicitly confirming the command name.</p>
</li>
<li><p>C: We asserted against the expected outcome, not the command output. Once again, we can change this output message at any time. It won’t affect the use case itself. The test case also plays a role in documenting the use case behavior. To better understand the use case, we can break it down into steps:</p>
</li>
</ul>
<ol>
<li><p>We need a command</p>
</li>
<li><p>That will take a CSV file path</p>
</li>
<li><p>When we run this command, there will be corresponding database entries, generated from the CSV file.</p>
</li>
</ol>
<p>This is a simple example, but you can already see the big picture from it. It’s much easier to think about smaller abstractions when you have that. Test-Driven Development (TDD) helps us see the big picture.</p>
<h3 id="heading-why-do-we-even-need-smaller-abstractions">Why Do We Even Need Smaller Abstractions?</h3>
<p>The short answer is you don’t always need them. Sometimes, a simple use case is more than enough. Then, there are other times when your use case gets complicated and harder to maintain. It starts to hold different types of behaviors. It gets complicated. That’s usually the signal to start abstracting it into smaller units. There is no clear line between both decisions. You, as a software architect, need to draw that line between them. Running a business is never easy, and neither is the process of abstracting its problems. We need to find ways to decompose those complex problems into smaller, simpler ones. This approach helps us in validating the behavior of each small solution individually.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1706510808091/f980ec52-3d3b-4dad-a3b7-5b0ef4049ae7.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-strategic-abstraction">Strategic Abstraction</h2>
<p>If your use case is simple enough, such as our creating post example, you don’t have to extract it into a service. There is no real benefit to communicating through that service. This separation adds an unnecessary layer of complexity. You’re creating unnecessary dependencies. Dependencies aren’t just about third-party libraries; you also need to manage dependencies between components. It only makes sense to abstract your problem away if it’s complex enough. If you are unable to solve it, then try abstracting it into smaller problems.</p>
<p>If you remember the “real-world” example of <code>CreatePost</code> from the previous article, we abstracted the part that converts Markdown to HTML there. It’s already a big problem, not necessarily what we need to solve in the <code>CreatePost</code> use case. Now, our use case depends on that abstraction. Additionally, there is one abstraction that is almost a de facto industry standard for all Domain-Driven Design projects—it’s called “Repository”. I’m pretty sure you know about it. Have you ever asked yourself why we abstract this part of our applications, almost in all projects? The answer is simple: because we benefit from it. They encapsulate data access logic and help us focus on the domain rules and behavior. They protect the domain model from knowing the details of how we store or retrieve data. When we abstract this layer of details, it becomes really easy to test our use cases. We replace that repository abstraction with an <code>InMemory</code> implementation. So we don’t need to run a whole datastore infrastructure to validate our use cases. That abstraction solves a complex problem for us. In this case, the trade-off we’re taking for abstracting that layer is paying us off. That’s what you should always focus on.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1706510819289/17c08b52-3560-4b71-855d-81b6388786af.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In the reference image above, there are several domain concepts, such as Service, Entity, ValueObject, Factory, and more. I won’t go into detail on each of them, as there are many learning resources available online. I want to help you understand the idea behind Domain-Driven Design (DDD) and those abstractions, rather than simply teaching individual technical concepts.</p>
<p>As we come to the end of this series, I hope you have learned something valuable from it! Feel free to share your thoughts in the comments! I will continue to work on examples for the Blogging Platform and share them as an open-source project on my <a target="_blank" href="https://github.com/akmandev">GitHub profile</a>. I will publish articles on software-related topics every week.</p>
<p>If you enjoyed this article series and don’t want to miss the next one, let’s connect on <a target="_blank" href="https://twitter.com/akmandev">https://twitter.com/akmandev</a> and <a target="_blank" href="https://www.linkedin.com/in/ozanakman">https://www.linkedin.com/in/ozanakman</a> for more content like this!</p>
]]></content:encoded></item><item><title><![CDATA[Understanding Domain-Driven Design (Part 2)]]></title><description><![CDATA[Introduction
Hey there! Welcome back to Part 2 of the “Understanding Domain-Driven Design” series. It’s awesome to see the positive feedback from Part 1. After a decade in the industry, I’ve tried my best to explain what I learned about Domain-Driven...]]></description><link>https://compiler.blog/understanding-domain-driven-design-part-2</link><guid isPermaLink="true">https://compiler.blog/understanding-domain-driven-design-part-2</guid><category><![CDATA[Web Development]]></category><category><![CDATA[software development]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[software architecture]]></category><dc:creator><![CDATA[Ozan Akman]]></dc:creator><pubDate>Sun, 21 Jan 2024 14:35:46 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/26MJGnCM0Wc/upload/b50677de5652c840a847b7a37bbe874a.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>Hey there! Welcome back to Part 2 of the “Understanding Domain-Driven Design” series. It’s awesome to see the positive feedback from Part 1. After a decade in the industry, I’ve tried my best to explain what I learned about Domain-Driven Design (DDD). Today, we’re going to talk about applying Domain-Driven Design (DDD). <a target="_blank" href="https://compiler.blog/understanding-domain-driven-design-part-1">If you haven’t read Part 1 yet, I strongly suggest you read that first.</a></p>
<h2 id="heading-focusing-on-the-actual-problem">Focusing on The Actual Problem</h2>
<p>Every domain has its unique set of problems. Let’s name a few: the travel industry, trade industry, real estate industry, auto industry, finance and banking industry, airline industry, commerce industry, and the list goes on… They all represent complex spaces. Our job is to abstract and simplify their solutions into a digital environment. Therefore, we need to focus on what matters. We want to design our software in a way that technical problems won’t affect us.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1705845602185/27d2b696-56c5-4b70-a0a2-1e11d41dca3f.png" alt /></p>
<h2 id="heading-are-frameworks-that-bad">Are Frameworks That Bad?</h2>
<p>No, frameworks are not that bad. They’re just tools. They’re okay when you use them effectively. They’re there to help you. However, they become problematic when they dictate your project structure, dependencies, and development approach. They become problematic when you become too dependent on them. You shift your attention from your domain to the framework, a technical detail. When tackling your own issues, you go to the framework documentation to find the best solution. Frameworks don’t understand your problems and needs. What if they suggest bad practices? Have you ever thought about this? That’s not the way it should be. I’ll show you another way in this series. Please, keep up with me. I promise you a mind shift to Domain-Driven Design (DDD) and it will be easy! We will try to combine the best of both worlds.</p>
<h2 id="heading-use-case-driven-approach">Use Case-Driven Approach</h2>
<p>When modeling our domain, we will always think about its use cases. We will stick with the real-world concepts. Ask yourself, “What problem are you solving?”. For instance, imagine buying a flight ticket, before the computer era? Let’s break down this process step by step:</p>
<ol>
<li><p>You would visit a travel agency or airline office.</p>
</li>
<li><p>Ask about available flights and ticket prices.</p>
</li>
<li><p>Choose a flight.</p>
</li>
<li><p>Make the payment.</p>
</li>
<li><p>Get your paper ticket, or the ticket might be mailed to your address.</p>
</li>
</ol>
<p>This process is still the same, except now the agent is a website or a mobile app. All those steps are use cases. We still solve the same problem: buying a flight ticket. You search for flights online, view available flights and ticket prices, choose a flight, make the payment, and finally receive your ticket in your inbox. We have abstracted this entire process and made it more efficient and accessible with today’s digital systems. The <code>GetAvailableFlights</code> is a use case. The <code>BookFlight</code> is a use case. The <code>MakePayment</code> is another use case. We want to focus on abstracting this particular aspect of the business, initially. Not the routing, or not the caching. These are not even important, yet. We don’t serve any users. Caching is a concern for an application that has performance issues. We’re not there, yet.</p>
<p>This little mind shift will also affect how we write our tests. When we want to validate our use cases, we can always provide test doubles. You don’t have to run Redis or MySQL to prove that your use case–the important part, works as expected. You can create an in-house <code>InMemory</code> implementations for different concerns. This will help you to test all of your use cases, without setting up a single infrastructural component. This is how you can start a project: create one folder and open a text editor. No need to install anything yet. This is freedom. We can always replace the <code>InMemory</code> drivers with the actual implementations, in the production configuration, before we launch. We simply need to provide an interface; A connection point between the infrastructure and the domain. This is where the mental model of separation comes into play. Don’t worry if this sounds a bit complex. We will dive into this topic later.</p>
<h3 id="heading-good-software-architecture">Good Software Architecture</h3>
<p>There is an important nuance with this structure and design. You need to understand that, as a software architect, you have to keep it clean and teach others how to keep it clean. The changes made in the UI components shouldn’t affect the use cases. Also, the changes in the use cases shouldn’t affect the domain entities. If you make changes in a REST controller, they shouldn’t change how your business operates. You need to have a strong mental model of separation. The location of files, next to each other or in different folders, shouldn’t really affect this mental model. You can organize things in a way that makes sense. A good software architecture allows for changes without high costs. It’s the one where you can make changes without saying “We need to create this project from scratch”.</p>
<h3 id="heading-understanding-use-case">Understanding Use Case</h3>
<p>A Use Case is a simple set of instructions to the computer, describing our task. You minimize your contextual complexity within each Use Case. You solve a small problem. Also, to understand the same problem, anyone just needs to study a small folder. Isn’t that amazing? Just imagine how much less explanation you need when someone joins your team. And how much less cognitive load they will have. Let’s create our first Use Case, the <code>CreatePost</code>.</p>
<h3 id="heading-creating-a-use-case">Creating a Use Case</h3>
<pre><code class="lang-php"><span class="hljs-keyword">namespace</span> <span class="hljs-title">Platform</span>\<span class="hljs-title">ContentManagement</span>\<span class="hljs-title">CreatePost</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">DateTimeImmutable</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">PublishMate</span>\<span class="hljs-title">Platform</span>\<span class="hljs-title">ContentManagement</span>\<span class="hljs-title">PostRepository</span>;

<span class="hljs-keyword">final</span> readonly <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CreatePost</span>  
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params">  
        <span class="hljs-keyword">private</span> PostRepository $postRepository,  
    </span>) </span>{  
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">create</span>(<span class="hljs-params">
        <span class="hljs-keyword">string</span> $title,  
        <span class="hljs-keyword">string</span> $content,
    </span>): <span class="hljs-title">void</span> </span>{  
        $post = <span class="hljs-keyword">new</span> Post($title, $content);
        <span class="hljs-keyword">$this</span>-&gt;postRepository-&gt;save($post);
    }  
}
</code></pre>
<p>That’s it? Yeah, that’s it! I told you it would be simple. I can hear you saying, “Bullshit! A real app would be much more complex!”. Let’s make it a bit more like a real-world app.</p>
<pre><code class="lang-php"><span class="hljs-keyword">namespace</span> <span class="hljs-title">Platform</span>\<span class="hljs-title">ContentManagement</span>\<span class="hljs-title">CreatePost</span>;  

<span class="hljs-keyword">use</span> <span class="hljs-title">DateTimeImmutable</span>;  
<span class="hljs-keyword">use</span> <span class="hljs-title">PublishMate</span>\<span class="hljs-title">Platform</span>\<span class="hljs-title">ContentManagement</span>\<span class="hljs-title">Author</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">PublishMate</span>\<span class="hljs-title">Platform</span>\<span class="hljs-title">ContentManagement</span>\<span class="hljs-title">MarkdownConverter</span>; <span class="hljs-keyword">use</span> <span class="hljs-title">PublishMate</span>\<span class="hljs-title">Platform</span>\<span class="hljs-title">ContentManagement</span>\<span class="hljs-title">Post</span>;  
<span class="hljs-keyword">use</span> <span class="hljs-title">PublishMate</span>\<span class="hljs-title">Platform</span>\<span class="hljs-title">ContentManagement</span>\<span class="hljs-title">PostId</span>;  
<span class="hljs-keyword">use</span> <span class="hljs-title">PublishMate</span>\<span class="hljs-title">Platform</span>\<span class="hljs-title">ContentManagement</span>\<span class="hljs-title">PostRepository</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">PublishMate</span>\<span class="hljs-title">Platform</span>\<span class="hljs-title">Shared</span>\<span class="hljs-title">Event</span>\<span class="hljs-title">EventDispatcher</span>;  

<span class="hljs-keyword">final</span> readonly <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CreatePost</span>  
</span>{  
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params">  
        <span class="hljs-keyword">private</span> PostRepository $postRepository,  
        <span class="hljs-keyword">private</span> EventDispatcher $eventDispatcher,
        <span class="hljs-keyword">private</span> MarkdownConverter $markdownConverter,  
    </span>) </span>{  
    }  

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">create</span>(<span class="hljs-params">  
        Author $author,  
        <span class="hljs-keyword">string</span> $title,  
        <span class="hljs-keyword">string</span> $slug,  
        <span class="hljs-keyword">string</span> $contentMarkdown,  
        <span class="hljs-keyword">string</span> $summary,  
        <span class="hljs-keyword">string</span> $canonicalUrl,  
        ?<span class="hljs-keyword">string</span> $coverImageUrl = <span class="hljs-literal">null</span>,  
        DateTimeImmutable $createdAt = <span class="hljs-keyword">new</span> DateTimeImmutable(<span class="hljs-params"></span>),  
        DateTimeImmutable $publishedAt = <span class="hljs-keyword">new</span> DateTimeImmutable(<span class="hljs-params"></span>),  
    </span>): <span class="hljs-title">void</span> </span>{
        $htmlContent = <span class="hljs-keyword">$this</span>-&gt;markdownConverter-&gt;toHtml($markdownContent);

        $post = <span class="hljs-keyword">new</span> Post(  
            author: $author,  
            title: $title,  
            slug: $slug,  
            contentMarkdown: $contentMarkdown,
            contentHtml: $htmlContent,  
            summary: $summary,  
            canonicalUrl: $canonicalUrl,  
            coverImageUrl: $coverImageUrl,  
            createdAt: $createdAt,  
            publishedAt: $publishedAt,  
        );  

        <span class="hljs-keyword">$this</span>-&gt;postRepository-&gt;save($post);  

        <span class="hljs-keyword">$this</span>-&gt;eventDispatcher-&gt;dispatch(  
            <span class="hljs-keyword">new</span> PostCreated($post-&gt;id),  
        );  
    }  
}
</code></pre>
<p>This is more or less a real-world use case. I will simplify the other examples for the article's purposes, but you can still understand my point. Of course, your domain has many use cases, dozens if not hundreds, and they all require different complex solutions, but your code doesn’t have to be complex. Coding is a tool to simplify those complex problems.</p>
<h2 id="heading-testing-a-use-case">Testing a Use Case</h2>
<p>How do you ensure this use case works as expected? You create a test case. I usually start with a test case and then build my way up to the complete use case. Why? Because this helps me design for the expected outcome. This is called <a target="_blank" href="https://martinfowler.com/bliki/TestDrivenDevelopment.html">Test-Driven Development (TDD)</a>. It’s a technique that is often misunderstood. It’s not just about getting your test coverage to 100%. Ironically, it’s not only even about testing; it is also a design approach that helps you understand how the computer executes your instructions. I usually follow the AAA pattern:</p>
<ul>
<li><p><strong>Arrange</strong> refers to the initial setup of your use case. This could be something like “I need a repository to save a post” or “I need an author to create a post”. Essentially, it’s about the requirements to execute the task.</p>
</li>
<li><p><strong>Act</strong> refers to the actual execution.</p>
</li>
<li><p><strong>Assert</strong> means comparing the expected outcome to the actual result to ensure they align.</p>
</li>
</ul>
<p>Let’s apply each step:</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">it_creates_blog_post</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span>
</span>{
    <span class="hljs-comment">// Arrange</span>
    $postRepository = <span class="hljs-keyword">new</span> InMemoryPostRepository();
    $useCase = <span class="hljs-keyword">new</span> CreatePost($postRepository);
}
</code></pre>
<p>Now that we have arranged the requirements, we can execute the task.</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">it_creates_blog_post</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span>
</span>{
    <span class="hljs-comment">// Arrange</span>
    $postRepository = <span class="hljs-keyword">new</span> InMemoryPostRepository();
    $useCase = <span class="hljs-keyword">new</span> CreatePost($postRepository);

    <span class="hljs-comment">// Act</span>
    $useCase-&gt;create(<span class="hljs-string">'Title'</span>, <span class="hljs-string">'Content'</span>);
}
</code></pre>
<p>We can finally verify if the post was actually created.</p>
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">it_creates_blog_post</span>(<span class="hljs-params"></span>): <span class="hljs-title">void</span>
</span>{
    <span class="hljs-comment">// Arrange</span>
    $postRepository = <span class="hljs-keyword">new</span> InMemoryPostRepository();
    $useCase = <span class="hljs-keyword">new</span> CreatePost($postRepository);

    <span class="hljs-comment">// Act</span>
    $useCase-&gt;create(<span class="hljs-string">'Title'</span>, <span class="hljs-string">'Content'</span>);

    <span class="hljs-comment">// Assert</span>
    $actualPost = $postRepository-&gt;getLastCreatedPost();
    $expectedPost = Post::createWithId($actualPost-&gt;id, <span class="hljs-string">'Title'</span>, <span class="hljs-string">'Content'</span>);

    <span class="hljs-built_in">self</span>::assertEquals($expectedPost, $actualPost);
}
</code></pre>
<p>Here we go—simple and easy. Once we run the test, we can confirm that our use case leads to the expected outcome. It is essential to test the behavior of the use case, not the implementation details. For instance, if you’re testing a database query, you need to ensure that the result is correct when you execute it. You don't need to validate if the SQL query is correctly written. Automated testing is a complex topic, and unfortunately, this is the only explanation I can provide in this series. But don’t worry, there’s good news! I plan to create an extensive guide about automated testing, too!</p>
<h3 id="heading-delaying-important-decisions">Delaying Important Decisions</h3>
<p>Good architecture allows you to delay major decisions like “What database are we going to use?”, “Which ORM tool should we pick?”, “What framework should we adopt?”. These are not the questions you should ask at the beginning of your project. They are tools, and you can pick them up whenever you need them. They should be plug-and-play. They shouldn’t dictate how you operate. This model enables you to replace any infrastructure parts without creating a huge fuss. As you might have noticed, we used the <code>InMemoryPostRepository</code>, which implements the <code>PostRepository</code> interface, just to validate our use case. We delayed the decision to choose a database. The Use Case-Driven Approach lets you focus on coding the important part rather than worrying about which tool to choose.</p>
<h2 id="heading-gluing-use-cases-and-frameworks">Gluing Use Cases and Frameworks</h2>
<p>Now that we have isolated our use case, we are free to choose any framework or tool we want. The process is simple: just inject the use case as a dependency and execute with the parameters. We still need to keep frameworks at a distance; we don't want to mix tools with our business. We want to be able to replace them whenever needed. Let’s create a basic Laravel controller using our use case:</p>
<pre><code class="lang-php"><span class="hljs-keyword">namespace</span> <span class="hljs-title">App</span>\<span class="hljs-title">Http</span>\<span class="hljs-title">Controllers</span>;

<span class="hljs-keyword">use</span> <span class="hljs-title">App</span>\<span class="hljs-title">Models</span>\<span class="hljs-title">User</span>;
<span class="hljs-keyword">use</span> <span class="hljs-title">Illuminate</span>\<span class="hljs-title">View</span>\<span class="hljs-title">View</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CreatePostController</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Controller</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__construct</span>(<span class="hljs-params">
        <span class="hljs-keyword">private</span> CreatePost $createPostUseCase,
    </span>) </span>{  
    }

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createPost</span>(<span class="hljs-params">Request $request</span>): <span class="hljs-title">View</span>
    </span>{
        <span class="hljs-keyword">$this</span>-&gt;createPostUseCase-&gt;createPost(
            $request-&gt;input(<span class="hljs-string">'title'</span>), 
            $request-&gt;input(<span class="hljs-string">'content'</span>),
        );

        <span class="hljs-keyword">return</span> view(<span class="hljs-string">'post.created'</span>);
    }
}
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Great job finishing Part 2! We’ve learned about the Use Case-Driven Approach, and Test-Driven Development (TDD). Feel free to share your thoughts in the comments! I hope to publish every week, on Sunday. In the next part, we will focus more on the details such as domain entities, events, and other abstractions, and discuss ways to simplify them.</p>
<p>If you enjoyed this article, let’s connect on <a target="_blank" href="https://twitter.com/akmandev">https://twitter.com/akmandev</a> and <a target="_blank" href="https://www.linkedin.com/in/ozanakman">https://www.linkedin.com/in/ozanakman</a> for more content like this!</p>
]]></content:encoded></item><item><title><![CDATA[Understanding Domain-Driven Design (Part 1)]]></title><description><![CDATA[Introduction
Hey there! Today, I’m explaining a good way of developing enterprise software. I’ve spent roughly 10 years in the industry and experienced many different architectures.
Let me be honest with you, this series won’t be useful if you’re onl...]]></description><link>https://compiler.blog/understanding-domain-driven-design-part-1</link><guid isPermaLink="true">https://compiler.blog/understanding-domain-driven-design-part-1</guid><category><![CDATA[Programming Blogs]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[software development]]></category><category><![CDATA[software architecture]]></category><category><![CDATA[Software Engineering]]></category><dc:creator><![CDATA[Ozan Akman]]></dc:creator><pubDate>Sun, 14 Jan 2024 11:04:37 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/3V8xo5Gbusk/upload/365e0ae5e6b5052fe37b53790824a91c.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>Hey there! Today, I’m explaining a good way of developing enterprise software. I’ve spent roughly 10 years in the industry and experienced many different architectures.</p>
<p>Let me be honest with you, this series won’t be useful if you’re only looking for short-term–rapid application development. I emphasize “short-term” because this is actually faster in the long run. Why? Well, as your product grows to a couple dozen of use cases (if not a hundred), you’ll find yourself fixing a lot of issues. This can slow down your progress in many ways: the release of new features, and bug fixes, onboarding of a new programmer, refactoring, and eventually, creating a bad experience for both you and your users. We’ll explore these issues in detail.</p>
<h2 id="heading-what-is-domain-in-domain-driven-design">What is “Domain” in Domain-Driven Design?</h2>
<p>In Domain-Driven Design (DDD), “domain” means the main area or topic that a software system is about. For instance, Facebook is about the social media domain, while Duolingo is about language and education. It’s the area we want to turn into software. This domain represents the real-world concepts, rules, and processes related to that space. The goal is to understand and model the complexities of that space in software. We want to ensure the software meets the requirements and behaves correctly, according to the domain.</p>
<p>In short, Domain-Driven Design helps you understand your business and users, and create software around their needs. Not influenced by your favorite framework or the preferences of IT people. End-users will never say “Oh look, they’re using the latest JavaScript framework!” unless your target audience consists solely of web developers.</p>
<h2 id="heading-who-is-a-domain-expert">Who is a Domain Expert?</h2>
<p>Domain experts are individuals with extensive knowledge in a particular field. In healthcare, these experts could be doctors or nurses. In finance, they might be economists. These experts give useful advice during the software design process. Close collaboration with others helps software developers understand the specific knowledge required to create software, instead of just imagining how things should work. This teamwork ensures that the software aligns with real-world needs and functions optimally. If you’re involved in creating a product for a certain time and making decisions within that area, you too are a domain expert!</p>
<h2 id="heading-why-is-domain-driven-design-important">Why is Domain-Driven Design Important?</h2>
<p>Domain-Driven Design (DDD) is important because it helps people to understand key questions like, “What are we solving?”, “Why are we solving this?”, “Who is this for?”. It helps you to understand that you’re solving people’s problems, not computer problems. By abstracting the domain and using it as the foundation for a software project, we can avoid being tied to specific technical terms, frameworks, or libraries. We can change any other parts of the project, as our foundation — the abstracted domain will remain strong. This means our software reflects real-world situations. We can have a shared understanding between our business and technical teams. This method makes it easier for software architectures to adapt and be modular, letting software systems change as requirements change.</p>
<h2 id="heading-practical-examples-design-a-blogging-platform-with-domain-driven-design-ddd">Practical Examples: Design a Blogging Platform with Domain-Driven Design (DDD)</h2>
<p>Throughout some examples, you’ll realize that you have more freedom than you might expect, particularly when compared to a framework-centric approach.</p>
<p>We’ll be considering a few different strategies, including Screaming Architecture (also known as <a target="_blank" href="https://blog.cleancoder.com/uncle-bob/2011/09/30/Screaming-Architecture.html">Use-case Driven Approach</a>), Test-Driven Development (<a target="_blank" href="https://martinfowler.com/bliki/TestDrivenDevelopment.html">TDD</a>), and a bit of Command Query Responsibility Segregation (<a target="_blank" href="https://martinfowler.com/bliki/CQRS.html">CQRS</a>). Don’t worry if you don’t know these terms, they sound fancy — they’re all tools designed to help solve software design problems.</p>
<p>As for the examples, I’ll be using PHP, which is my language of choice. However, the beauty of DDD is that it’s not about the language or the framework. It’s about designing a software system. So feel free to use any language you’re comfortable with. But before we start, please forget about how you learned to develop software with frameworks. The first thing you will do would be to install a framework because you will need routing, authentication, or validation, right? Not really, you don’t even have one user, yet. First, you need to abstract your domain away. You don’t need any frameworks or libraries other than a testing framework. Something that helps you to prove your use case works as expected.</p>
<h3 id="heading-file-structure">File Structure</h3>
<p>Software design isn’t just about playing with files and folders, arranging them like they’re puzzle pieces. Many developers get stuck in the habit of thinking that designing software is all about arranging files in a certain pattern. What about frameworks? They’re very flexible but they still force their own semantics. They provide a predefined structure, rules, and many different tools, no questions asked, whether you’re building a big blogging platform or a calculator app.</p>
<p>We’re not jumping into picking a framework right off the bat. Sure, it’s convenient to have folders like <code>App/Models</code>, <code>App/Controllers</code>, or <code>App/Events</code> for your small projects. It’s like a cozy house where everything has its place. But when it comes to expanding an enterprise-level project with hundreds of use cases, this cozy house rapidly turns into a complex maze. Can you imagine a newcomer trying to navigate through this labyrinth? The confusion would lead to endless questions and slow progress.</p>
<p>So, instead of starting with a framework, we’ll take a different route. We’ll start by defining our domain model and Bounded Contexts.</p>
<h3 id="heading-bounded-context-the-building-blocks">Bounded Context: The Building Blocks</h3>
<p>When we talk about Domain-Driven Design (DDD), we use the term “Bounded Context” to describe different modules of our software system. Each Bounded Context is unique, focusing on its own tasks, rules, responsibilities, and concepts.</p>
<p>Let’s consider designing a blogging platform. What are the main components? You might think of elements like the blog posts, the authors, comments, and social interactions such as giving a like or dislike. Each of these elements addresses specific aspects. Let’s inspect each one more closely and give them specific names.</p>
<ul>
<li><p>Content Management: This context manages all the content-related entities such as <code>Blog Posts</code> and <code>Tags</code>.</p>
</li>
<li><p>Comment: This context handles all entities related to comments, such as <code>Comments</code>, <code>Comment Replies</code>, and <code>Comment Feedback (Likes and Dislikes)</code>.</p>
</li>
<li><p>Author Profile: This context manages the <code>Author</code> profile page (Bio, profile picture, etc.), and <code>Followers</code>.</p>
</li>
<li><p>Social Interaction: This context covers interactions such as <code>Bookmarking</code>, <code>Post Feedback (Likes and Dislikes)</code>, and <code>Subscriptions (Follow and Unfollow)</code>.</p>
</li>
<li><p>User Management: This context takes care of user-related entities such as <code>Settings</code>, <code>Privacy</code>, <code>Notifications</code>, and <code>Premium Subscription</code>.</p>
</li>
</ul>
<h4 id="heading-a-couple-of-insights">A couple of Insights</h4>
<p>Something is interesting, the <code>User</code> represents the same person, but we treat them differently in different Bounded Contexts. Why is that?</p>
<ul>
<li><p>In the <code>User Management</code> context, a <code>User</code> can modify their personal settings, adjust privacy preferences, and manage notification settings.</p>
</li>
<li><p>In the <code>Content Management</code> context, the same <code>User</code> is represented as an <code>Author</code> who creates, edits or deletes blog posts.</p>
</li>
<li><p>On the other hand, in the <code>Social Interaction</code> context, a <code>User</code> might be someone who bookmarks posts, likes posts or follows other authors.</p>
</li>
</ul>
<p>These actors may even share the same name, but they are three distinct actors, each with their own attributes, relationships, and rules within their respective contexts.</p>
<h3 id="heading-visualizing-bounded-contexts-project-structure">Visualizing Bounded Contexts: Project Structure</h3>
<p>One major change we’re making is reconsidering how we organize files and folders. Traditionally, developers have followed specific patterns for arranging them, which is largely dictated by the chosen framework. This approach is not the way for every software project. As we’ve discussed, Domain-Driven Design (DDD) focuses on the purpose of the software, which means our file structure needs to reflect our domain model, not the other way around. So, how do we translate the idea of Bounded Contexts into a practical file structure?</p>
<p>Let’s begin by creating a separate folder for each Bounded Context in our blogging platform. These folders will serve as the individual homes for each context’s unique rules, tasks, and concepts.</p>
<pre><code class="lang-plaintext">└── src
    ├── AuthorProfile
    ├── Comment
    ├── ContentManagement
    |   ├── Post.php
    |   ├── PostRepository.php
    ├── SocialInteraction
    └── UserManagement
</code></pre>
<p>Here, each folder represents a Bounded Context. Even from this high-level view, you can understand the domain of this application without needing to dive deep into the codebase. This is the essence of Screaming Architecture. So, rather than screaming “This is a Laravel app!”, it screams “This is a blogging platform!”.</p>
<p>But hold on, what about routing? Don’t we need a framework or library for that? Let’s take a step back and remember that we are still in the process of defining our domain and use cases. We are not serving any users, yet.</p>
<h4 id="heading-ddd-layers-domain-application-infrastructure">DDD Layers: Domain, Application, Infrastructure</h4>
<p>In the DDD world, usually, the applications are layered into the domain, the application, the infrastructure, and perhaps the presentation. We’ve already discussed the domain layer—it’s the heart of our software, encapsulating the business rules and entities.</p>
<p>The application layer is like the conductor of our software orchestra; it uses the building blocks provided by the domain layer to accomplish specific tasks. Consider a task like <code>PlayMusic</code>, the application layer coordinates the domain objects like <code>Music</code> or <code>Artist</code> to execute this task, guiding the process from start to finish.</p>
<p>The infrastructure layer, on the other hand, is like the stage where our software orchestra performs. It provides the instruments required by the domain and application layers to function effectively. It supports the performance without dictating the music.</p>
<p>The presentation layer is where you perform your gig. It's the concert hall.</p>
<p>Imagine a scenario where your orchestra is restricted to playing in only one place—a stage where you can't move or replace a chair or an instrument. That’s what it feels like when you start your project by choosing a framework. You’re tied to that stage, and any attempt to change a component could break the whole setup. Instead, you should be able to replace infrastructural components at any time without causing a catastrophic failure.</p>
<p>Now, let’s address a common misconception: while these layers are key to designing your software, they don’t necessarily need to directly translate into your folder structure.</p>
<pre><code class="lang-plaintext">└── UserManagement
    ├── Application
    ├── Domain
    └── Infrastructure
</code></pre>
<p>Many developers, myself included, have understood these layers as something to be explicitly represented in the codebase, resulting in folder structures like the one above. While this structure is often more manageable than a purely framework-based one or the traditional MVC folders, it still misses the point: DDD is not about folder structures, it’s about software design. The layers should guide your software design as a principle in your mind; it doesn’t have to be a literal representation in your folders. Simply put, you should have a clear mental picture of this layered architecture; it doesn’t have to be exactly reflected in a folder structure.</p>
<p><a target="_blank" href="https://cdn.hashnode.com/res/hashnode/image/upload/v1705227661029/15a6ab4b-2fd5-4cbe-92e8-281481aa7ec5.png?auto=compress,format&amp;format=webp"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1705227661029/15a6ab4b-2fd5-4cbe-92e8-281481aa7ec5.png" alt /></a></p>
<p>Now, let’s take a moment to consider the folder structures above – whether you’re an experienced developer or not, think about which one truly makes it easier to maintain the code?</p>
<h3 id="heading-our-first-use-case-creating-a-blog-post">Our First Use Case: Creating a Blog Post</h3>
<p>The first step in designing our blogging platform using Domain-Driven Design (DDD) is to focus on the use cases. Before we dive into routing, logging, or caching, let’s remember that these are infrastructural concerns and can be easily replaced. We shouldn’t be bound to specific tools; after all, tools are meant to assist us in achieving our goals.</p>
<p>Let’s start by learning the blueprint of our application. The journey begins from a Bounded Context and then drills down into specific use cases. You have the option to create additional folders to separate sub-contexts, like <code>src/ContentManagement/Post/CreatePost</code>, instead of <code>src/ContentManagement/CreatePost</code>. That’s totally fine! The key here is ensuring the folder structure is easy to understand and makes sense to anyone who looks at the codebase. It should focus on the domain logic rather than being overly technical or grouped by file types. We want it to be clear and straightforward so that developers can quickly grasp how everything is organized. With this approach, we can gain a clearer view of our initial design before refining it further.</p>
<pre><code class="lang-plaintext">└──📁src
    └──📁Bounded Context
        ├──📁Use Case 1
        ├──📁Use Case 2
        ├──📄Context Object 1 (e.g. Domain Entity)
        ├──📄Context Object 2 (e.g. Repository Interface)
        └──📄Context Object 3 (e.g. Aggregate Root)

# This translates into something like the following in real-world projects.

└──📁src
    └──📁ContentManagement
        ├──📁CreatePost
        |     ├──📄CreatePost.php
        |     ├──📄PostCreated.php
        ├──📁GetPost
        ├──📄Author.php
        ├──📄Post.php
        ├──📄PublishedPosts.php
        ├──📄PostRepository.php
        └──📄PostRepositoryUsingDoctrine.php
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Great job finishing Part 1! We’ve learned important concepts like Domain, and Bounded Context. Let me know what you think in the comments! I hope to publish every week, on Sunday. In the next part, I'm aiming to provide more examples and practical use cases. I want to ensure everything is easy to understand.</p>
<p>If you enjoyed this article, let’s connect on <a target="_blank" href="https://twitter.com/akmandev">https://twitter.com/akmandev</a> and <a target="_blank" href="https://www.linkedin.com/in/ozanakman">https://www.linkedin.com/in/ozanakman</a> for more content like this!</p>
]]></content:encoded></item><item><title><![CDATA[Why is it difficult for many people to create things consistently?]]></title><description><![CDATA[Hey there, today is not about tech talk. I've been thinking about some drafts that have been sitting around untouched for a while. I asked ChatGPT why it's so challenging to create things consistently, but its responses were quite basic, as usual. Th...]]></description><link>https://compiler.blog/why-is-it-difficult-for-many-people-to-create-things-consistently</link><guid isPermaLink="true">https://compiler.blog/why-is-it-difficult-for-many-people-to-create-things-consistently</guid><category><![CDATA[AI]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[software development]]></category><category><![CDATA[Programming Blogs]]></category><dc:creator><![CDATA[Ozan Akman]]></dc:creator><pubDate>Sat, 06 Jan 2024 17:26:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/58Z17lnVS4U/upload/58400462003e06dfa23b5dd69eb41f64.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hey there, today is not about tech talk. I've been thinking about some drafts that have been sitting around untouched for a while. I asked ChatGPT why it's so challenging to create things consistently, but its responses were quite basic, as usual. The question I'm trying to answer is, “Why is it difficult for many people to create things consistently?”.</p>
<p>Anyway, I just figured it out, it's difficult because it's not fun. It's not engaging at all. Staring at a blank screen and trying to put thoughts into words? Not the most thrilling activity. Expressing what's in your head? Tough stuff. Even experts struggle to explain what they know. Not only that, doing consistent open-source work, or developing your side project in your free time. All of that is difficult.</p>
<p>It's not just difficult; it's made even more difficult by the complexities of modern life, and all the distractions like social media, Netflix, or video games. Imagine, you have a family with kids. Just thinking about it can be overwhelming, let alone finding the focus. But you know what? Some folks still manage it. How? Well…</p>
<h2 id="heading-how-do-other-people-do-it">How do other people do it?</h2>
<p>I'll tell you straight ahead. The people I know, they don't really care if something's boring or exciting—they push through regardless. They have control over themselves, and their mind. They look from the perspective, “Okay, this needs to be done”. Sounds easy, but it isn't. This requires discipline. Being able to concentrate, even when you'd rather be doing something else, is an amazing skill. It's kind of what meditation practices teach. So how do you improve that skill?</p>
<h2 id="heading-how-can-you-focus-on-boring-tasks">How can you focus on boring tasks?</h2>
<p>Our knowledge about our brains today is quite remarkable. I'm not an expert in that area, but I've been diving into neuroscience and the human body lately. Essentially, you've got to train your brain for this. It would help if you learn how to relax and how to lower your stress level, so you have space for other things. I'm pretty sure, you had those days when you stressed yourself with something, out of nowhere. Well, guess what? You can do the exact opposite. You just need to learn how. If you're interested in this topic, I would recommend Wim Hof's breathing technique, it's amazing. I would also suggest listening to a podcast or watching a clip from people like Andrew Huberman. Meditation is another great tool. Sitting there in a stance, and focusing on yourself, telling your mind what to do. It seems boring at first, but it's insanely effective. That's why meditation practices have been passed down through generations. Because it works! The cold showers are fantastic. There are tons of scientific evidence behind it. If those seem extreme, start with taking a walk, regularly. So, if you're curious, dive into all this stuff, especially on YouTube if videos are more your thing!</p>
<p>Oh, by the way, our brains aren't the only important bits in our bodies, everything is connected. So, taking care of your whole body is crucial for your brain to perform at its peak. We have a proverb in Turkish that goes something like “A healthy mind in a healthy body”. Exercise regularly, maintain a healthy diet, and ensure good sleep. Don't try to cheat on your body. What are you trying to save? Time or money? At the expense of your own well-being? Take the time to prepare a wholesome meal. Understand your body and its needs.</p>
<h2 id="heading-understand-yourself">Understand yourself</h2>
<p>Creating regularly can be tough, no doubt about it. But remember, every small step counts. Embracing the challenge and staying disciplined, even when it feels hard, leads to wonderful growth. Understand, and don't be so hard on yourself. Be flexible when you need to. Learn how to love the process, not only the outcome. The struggle today becomes the strength tomorrow. If you improve by 1% every week for a year, you will be approximately 164.7% better by the end of the year compared to where you started. You've got this!</p>
<h2 id="heading-stay-connected">Stay connected</h2>
<p>Thank you for reading! If you enjoyed this article, consider following me on <a target="_blank" href="https://twitter.com/akmandev">https://twitter.com/akmandev</a> and <a target="_blank" href="https://www.linkedin.com/in/ozanakman">https://www.linkedin.com/in/ozanakman</a> for more content like this. I'm trying to regularly share insights on software engineering that can help you improve your skills and stay up-to-date with the latest trends in the industry. I appreciate your support and hope to connect with you soon!</p>
]]></content:encoded></item><item><title><![CDATA[Building Beyond Tools: What's Wrong with Modern Framework-based Development?]]></title><description><![CDATA[The Road to True Expertise: More Than Just Tools
In modern programming, languages and tools have made it easier than ever to code. You can create a web project without spending four to six years buried in textbooks and lectures. I believe I could tea...]]></description><link>https://compiler.blog/building-beyond-tools-whats-wrong-with-modern-framework-based-development</link><guid isPermaLink="true">https://compiler.blog/building-beyond-tools-whats-wrong-with-modern-framework-based-development</guid><category><![CDATA[Programming Blogs]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Laravel]]></category><category><![CDATA[PHP]]></category><dc:creator><![CDATA[Ozan Akman]]></dc:creator><pubDate>Sat, 13 May 2023 08:02:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/8Gg2Ne_uTcM/upload/6bdcc72b697fa3a7cc32fcdc1f0ee1d8.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-the-road-to-true-expertise-more-than-just-tools">The Road to True Expertise: More Than Just Tools</h2>
<p>In modern programming, languages and tools have made it easier than ever to code. You can create a web project without spending four to six years buried in textbooks and lectures. I believe I could teach anyone the basics of web programming in just six short months. However, don't mistake a quick education for mastery. True expertise is earned through years of work, endless failures, and a constant thirst for knowledge. It takes years of practice, study, and engagement with the broader community to truly become an expert in our field. Have you ever tried to explain a difficult concept to an inexperienced developer? Described a solution to a complex problem? Lead group of developers? Documented your code in a way that empowers your colleagues to build on your work? Simply spending time in the industry or having a successful Youtube channel doesn't make you an expert. True expertise comes from deep knowledge and the ability to communicate effectively.</p>
<p>People who are intelligent and experts in their field, tend to question everything with an open mind and come up with logical reasoning. People who are inexperienced or lack knowledge often have strong opinions and beliefs and are not open to new ideas or ways of thinking. Approach problem-solving with an open mind, question assumptions, and follow the evidence. This is what leads to success in any field, not just intelligence or experience alone.</p>
<h2 id="heading-dont-start-your-project-by-picking-a-framework">Don't Start Your Project by Picking a Framework</h2>
<p>As software developers, we are constantly bombarded with new and exciting tools that promise to make our lives easier. While frameworks can be useful tools, they can also lead us down the path of technical debt if we're not careful. The problem with frameworks is that they come with a lot of assumptions about how your application should be structured and what features it should have.</p>
<p>Write code around the core of your business idea, also known as business logic, rather than around the tools. Let's say you're working on an e-commerce project. Your first concerns should be related to product pricing, inventory management, order processing, and shopping cart. You don't need to worry about user authentication, authorization, or email sending at this stage. By building your foundation around your business logic, you can choose whatever tool or framework you prefer for the infrastructure or UI layer concerns.</p>
<p>Another example is a social media application, where your focus should be on user profiles, messaging, and content moderation, without concerning yourself with error reporting or caching. Understanding this concept will help you see tools as replaceable parts of your project. It's important to keep in mind that framework creators tend to market their products as must-have tools, and their documentation and tutorials often emphasize using their specific tools for project creation. But with a solid understanding of business logic and the needs of your application, you can choose the right tools to support your project and bring it to life.</p>
<p>That's where abstraction comes in. Abstraction is the art of simplification - of breaking down complex systems into simpler, more manageable parts. By abstracting away the details of your application's implementation, you can create a more flexible, adaptable architecture that can evolve over time. When we build our application around a framework, we are essentially taking away this flexibility and building on a set of technical debts that we may not fully understand.</p>
<h2 id="heading-rapid-application-development-doesnt-solve-all-your-problems">Rapid Application Development Doesn't Solve All Your Problems</h2>
<p>Rapid application development is encouraged in some communities, such as the Laravel community. They show examples of small teams or solo developers making a lot of money. Tempting. However, this doesn't mean they are creating high-quality software. They usually sacrifice good architecture in order to start their business quickly. And, as they're solo developers, I completely understand. It is effective for them. The problem is that they promote this approach as the way to go! Being a good software architect is not the same thing. It's important to remember that simply knowing how to code does not qualify someone as an industry-leading expert. As I previously stated, anyone can learn the basics of web programming in as little as six months.</p>
<p>Some people may think that building an application based on business logic is slower than using specific tools. But, it's not always the case. Although it may take some extra time, to begin with, it makes it easier to maintain the code in the long run. Also, you don't need to hire a specialized developer who knows a specific tool, any good developer can understand and work with your code. This saves time and increases flexibility.</p>
<p>Another benefit of this approach is that the developer will better understand your business. This helps them to contribute better to the project and become a partner in your business's success. Although it may require a little extra work at the beginning, prioritizing business logic over specific tools can have many advantages in the long run.</p>
<p>In the initial stages of a software project, it's crucial to define the problem you're trying to solve and the objectives you aim to achieve with your solution. By asking these fundamental questions, you can shape your architecture effectively. To this end, it's essential to identify your use cases. A use case is a particular scenario or situation in which your software will be used. This technique helps to ensure that your software satisfies the unique needs of your users, thereby enabling you to create high-quality software that meets their expectations.</p>
<h2 id="heading-whats-more">What's More?</h2>
<p>As you are, I am also a learner. Every day, I'm eager to learn what the best approach is. Trying, not to have strong opinions and question everything objectively.</p>
<p>I am planning to create a series of articles with a lot of code examples. Additionally, I will provide a solid open-source project that you can explore to see how those examples fit into a real project. Let's stay connected!</p>
<p>Meanwhile, I suggest you read <a target="_blank" href="https://blog.cleancoder.com/uncle-bob/2011/09/30/Screaming-Architecture.html">Screaming Architecture</a> by Robert C. Martin (Uncle Bob).</p>
<p>Thank you for reading! If you enjoyed this article, let me know your opinions in the comments section! You can follow me on <a target="_blank" href="https://twitter.com/akmandev">https://twitter.com/akmandev</a> and <a target="_blank" href="https://www.linkedin.com/in/ozanakman">https://www.linkedin.com/in/ozanakman</a>. I'm trying to regularly share insights on software engineering that can help you improve your skills and stay up-to-date with the latest trends in the industry. I appreciate your support and hope to connect with you soon!</p>
]]></content:encoded></item><item><title><![CDATA[Avoid These Mistakes in Your Software Company]]></title><description><![CDATA[Throughout my career as a software engineer, I have worked in many different kinds of situations. From small teams to some of the biggest in the industry. The successful ones all had one thing in common: they valued their employees as the cornerstone...]]></description><link>https://compiler.blog/avoid-these-mistakes-in-your-software-company</link><guid isPermaLink="true">https://compiler.blog/avoid-these-mistakes-in-your-software-company</guid><category><![CDATA[software development]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[WeMakeDevs]]></category><category><![CDATA[Programming Blogs]]></category><dc:creator><![CDATA[Ozan Akman]]></dc:creator><pubDate>Wed, 19 Apr 2023 08:34:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/-Cmz06-0btw/upload/5b74bdd297db2f1296b89d416761126e.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Throughout my career as a software engineer, I have worked in many different kinds of situations. From small teams to some of the biggest in the industry. The successful ones all had one thing in common: they valued their employees as the cornerstone of their business. In the fast-paced software world, companies might get so focused on delivery that they fail to see their own mistakes. We'll go over some typical mistakes I've faced before and wished to change while working at those companies.</p>
<h2 id="heading-1-not-investing-in-employees">1. Not investing in employees</h2>
<p>Employees are the backbone of any business. They form the foundation. It is critical for companies to invest in their employees' personal development. Because it is their skills, expertise, and motivation that result in high-quality products. They are an essential part of your company's success. A poorly trained or demotivated workforce, on the other hand, might result in decreased productivity, higher error rates, missed deadlines, and, ultimately, dissatisfied customers. Do not consider your employees as disposable operation units; doing so will lead to the failure of your organization.</p>
<h2 id="heading-2-not-prioritizing-automated-testing">2. Not prioritizing automated testing</h2>
<p>A lack of expertise can lead to deprioritizing or not understanding automated testing. The problem is that testing is an essential component of the software development process. Because it doesn't only ensure your product can run smoothly; it also helps identify and resolve issues before they become major problems. Without proper testing, your product will most likely suffer from a range of issues. Unexpected crashes, bugs, errors — you name it. This leads to a negative user experience. Therefore, frustrated users stop using your products. Some critical bugs can even lead to reduced sales or legal issues. Also, without proper testing, you can't refactor your legacy code. I talked about this topic in <a target="_blank" href="https://akman.hashnode.dev/refactoring-legacy-applications">one of my previous articles</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1681890258758/26103dea-eed3-4d74-a8b9-50bb066251cf.jpeg" alt="IBM System Science Institute Relative Cost of Fixing Defects" class="image--center mx-auto" /></p>
<h2 id="heading-3-failing-to-plan">3. Failing to plan</h2>
<p>Planning is a fundamental component of the software development process. It's often overlooked in favor of fast delivery. The pressure to release quickly can lead to rushed coding without sufficient planning, which can result in a range of issues. It can lead to scope creep, poor communication, and of course, delays. Poor communication results in poor collaboration, and so on. It will drain the limited resources you have, leading to inefficiencies and poor user experiences. Not only that, your employees won't be able to measure their impact and will always feel like they're failing. This will result in employee attrition.</p>
<p>If you're a small company, I would suggest that you not only listen to your managers but also listen to all of your employees.</p>
<h2 id="heading-4-ignoring-feedback">4. Ignoring feedback</h2>
<p>We're going to talk about two different types of feedback: employee feedback and customer feedback.</p>
<p>Employee feedback provides valuable insights into the development process and helps identify improvement areas. If you think you know it all as a manager or fail to take employee feedback seriously, thinking it's unimportant or irrelevant, this will reflect on your company. It will lead to a lack of engagement and motivation among team members. They will feel like their contributions are not valued, so they will contribute the bare minimum. Poor morale, reduced productivity, and high turnover rates result in a costly mistake for your company.</p>
<p>Besides that, ignoring user feedback is also an equally problematic mistake. Disconnecting your product from the needs of its end users can easily lead to a poor user experience and lost sales. You should use user feedback as a guide to the development process.</p>
<h2 id="heading-5-micromanagement">5. Micromanagement</h2>
<p>Micromanagement is an absurd management style where managers closely monitor and control those who report to them. I'm sure many of us have dealt with micromanagement at some point in our careers. This will result in a lack of trust between managers and staff, which will lead to less collaboration and communication. It will affect the quality of deliveries. Employees will be disengaged and demotivated. They will feel a lack of autonomy and control over their work. Eventually, there will be less creativity and innovation.</p>
<p>What actions can you take to avoid micromanagement? Simple: Trust in your employees. Encourage open communication. Listen to employee feedback and provide feedback regularly. Focus on the outcome. Rather than controlling, provide support and guidance.</p>
<p>Let me conclude this section with a Steve Jobs quote:</p>
<blockquote>
<p>The greatest people are self-managing -- they don't need to be managed. Once they know what to do, they'll go figure out how to do it. What they need is a common vision. And that's what leadership is: having a vision; being able to articulate that so the people around you can understand it; and getting a consensus on a common vision.</p>
</blockquote>
<h2 id="heading-stay-connected">Stay Connected</h2>
<p>Thank you for reading! If you enjoyed this article, consider following me on <a target="_blank" href="https://twitter.com/akmandev">https://twitter.com/akmandev</a> and <a target="_blank" href="https://www.linkedin.com/in/ozanakman">https://www.linkedin.com/in/ozanakman</a> for more content like this.</p>
<p>I'm trying to regularly share insights on software engineering that can help you improve your skills and stay up-to-date with the latest trends in the industry. I appreciate your support and hope to connect with you soon!</p>
]]></content:encoded></item><item><title><![CDATA[Programming paradigms: Which framework is better?]]></title><description><![CDATA[Programming is a very complex and rapidly evolving field, with new technologies and methodologies constantly emerging. One fundamental aspect of programming is the approach you choose to solve a very specific problem. As software development became a...]]></description><link>https://compiler.blog/programming-paradigms-which-framework-is-better</link><guid isPermaLink="true">https://compiler.blog/programming-paradigms-which-framework-is-better</guid><category><![CDATA[Programming Blogs]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[Laravel]]></category><category><![CDATA[PHP]]></category><dc:creator><![CDATA[Ozan Akman]]></dc:creator><pubDate>Sat, 11 Mar 2023 11:31:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/t2b2svMf8ek/upload/fe0c25a59a91d71c3c8bc4acc2cbbcb3.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Programming is a very complex and rapidly evolving field, with new technologies and methodologies constantly emerging. One fundamental aspect of programming is the approach you choose to solve a very specific problem. As software development became a lot more complex than before, we needed a set of pre-built components to help us accelerate our development speed across different projects.</p>
<h2 id="heading-lets-talk-about-frameworks">Let's talk about frameworks</h2>
<p>To tackle the problem of speed and efficiency, we developed several tools. Components, structures, libraries, and frameworks were all invented to help developers in minimizing time and effort while solving common problems. It's important to remember that we invented every tool to make our lives easier. Those common problems such as database connectivity, user authentication and authorization, and user interface design. We'll get to those later.</p>
<h2 id="heading-is-it-reasonable-to-prioritize-speed-above-all">Is it reasonable to prioritize speed above all?</h2>
<p>Speed and efficiency are indeed one of the most important motives for the invention of programming frameworks. However, there are some other key factors such as standardization and best practices, reducing the risk of errors and bugs, maintainability, reusability, scalability, and consistency. These concerns are critical to the success of modern applications.</p>
<h2 id="heading-what-works-for-you-may-not-work-for-me">What works for you may not work for me</h2>
<p>Different projects have different requirements, preferences, and circumstances. As the same concept applies to our daily lives, different people have different stories and preferences. What is right or effective for one person may not be so for another. Think about it. Including work, relationships, hobbies, and health. Some people thrive in high-pressure, fast-paced work environments. Others prefer a more relaxed approach. This concept also applies to programming frameworks. Different frameworks have different abilities, weak points, and design philosophies, so what works well for one project may not work well for another.</p>
<h2 id="heading-how-to-choose-a-framework">How to choose a framework?</h2>
<p>While choosing a framework, consider the specific needs and requirements of your project. Some frameworks may be better suited to small and simple projects, while others may be better suited to large and complex projects. Let us question our choice!</p>
<ul>
<li><p>Do you have a team with specialized knowledge and expertise?</p>
</li>
<li><p>Are you a solo developer or a small team?</p>
</li>
<li><p>Do you really need amazing architecture before you release your MVP?</p>
</li>
<li><p>Does Laravel or Symfony make it easier to find new team members?</p>
</li>
<li><p>What about using a different programming language? Should you consider Python: Django or Flask?</p>
</li>
<li><p>What about UI? React or Vue?</p>
</li>
</ul>
<p>You can also read <a target="_blank" href="https://medium.com/@ozanakman/symfony-is-not-the-best-php-framework-6123ee858fab">"Symfony is not the best framework"</a> which I wrote and believe answered the "Which framework is better" question.</p>
<h2 id="heading-software-engineering-is-all-about-trade-offs">Software engineering is all about trade-offs</h2>
<p>This list could go on forever. I've got at least ten more questions. Instead, I'll give you a personal example from my own life. Even though I work as a back-end developer for my team during the week, weekends are a completely different story. I work as a full-stack product engineer on my side project. I'm in charge of all decisions, the design process, and the development progress. I must accept that there will be some trade-offs. Let's talk about it!</p>
<ul>
<li><p>I only have a limited amount of time on weekends to develop software.</p>
</li>
<li><p>I just want something that works; it doesn't have to be perfect.</p>
</li>
<li><p>I'd like to get feedback from real users as soon as possible.</p>
</li>
<li><p>I don't need a full development cycle, as development teams do. Instead, I can plan a straightforward strategy.</p>
</li>
<li><p>I am comfortable with PHP and can work with any PHP framework.</p>
</li>
<li><p>I am familiar with TypeScript, JavaScript, and React.</p>
</li>
<li><p>I know how to write clean code.</p>
</li>
<li><p>I know how to write automated tests.</p>
</li>
<li><p>I am familiar with SQL and can create a database.</p>
</li>
<li><p>I'm aware of technologies that could be useful for other types of data storage.</p>
</li>
</ul>
<p>Wow, looks like I got all I need in my toolbox to build a product. I'm not the only one. I know so many devs that have a similar skill set. So, let's come back to the main question.</p>
<p>We use Symfony at work, and I love it! As a result, I decided to start my side project with it. With Symfony, I quickly realized that every decision costs me time. As explained previously, I have a limited amount of time on weekends. Symfony's flexibility also takes time to build.  Even though it's of good quality and helps you create good software, it wasn't the best fit for my needs. I only have so much time. I started to consider other options, such as Laravel. Even though I didn't agree with everything, I was impressed. It allowed me to build much faster. Will I refactor and switch to something else in the future?  That's exactly how it works.</p>
<p>The circumstances of today are not the same as those of tomorrow. It's not always black and white. Usually, I try not to overthink tomorrow's problems. However, I am not ignoring every fact that will cause more problems tomorrow. I would be happy to deliver, refactor, and upgrade my product between now and tomorrow. I would be pleased to have software that both benefits its users and profits. My only piece of advice is to choose a framework that you can deliver over!</p>
<h2 id="heading-want-to-read-more">Want to read more?</h2>
<p>Follow me on Twitter and LinkedIn to be the first to know about my next article!</p>
<ul>
<li><p><a target="_blank" href="https://twitter.com/akmandev">https://twitter.com/akmandev</a></p>
</li>
<li><p><a target="_blank" href="https://www.linkedin.com/in/ozanakman">https://www.linkedin.com/in/ozanakman</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Refactoring legacy applications]]></title><description><![CDATA[What's refactoring?
Refactoring is the process of improving the structure of code without changing its behavior. It can increase readability, maintainability, and performance. It can reduce technical debt. When a codebase is cluttered and outdated, t...]]></description><link>https://compiler.blog/refactoring-legacy-applications</link><guid isPermaLink="true">https://compiler.blog/refactoring-legacy-applications</guid><category><![CDATA[Programming Blogs]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[Beginner Developers]]></category><category><![CDATA[General Programming]]></category><dc:creator><![CDATA[Ozan Akman]]></dc:creator><pubDate>Thu, 09 Feb 2023 10:17:54 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/p0j-mE6mGo4/upload/59e672c8857894023e8f221dcf872298.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-whats-refactoring">What's refactoring?</h2>
<p>Refactoring is the process of improving the structure of code without changing its behavior. It can increase readability, maintainability, and performance. It can reduce technical debt. When a codebase is cluttered and outdated, there are two options: rewriting the code or refactoring it. While rewriting is easy for developers, it is very expensive. Refactoring can be done without disrupting internal and external processes. This article will guide you through the process of legacy code refactoring and highlight common pitfalls and how to avoid them.</p>
<h2 id="heading-steps-of-refactoring-a-legacy-codebase">Steps of refactoring a legacy codebase</h2>
<ol>
<li><p><strong>Understand</strong>: To begin, you must understand the current architecture. Take your time learning about its structure and how different parts interact with each other. Are there any patterns? Is there a reason for the complexity?</p>
</li>
<li><p><strong>Identify</strong>: Always look for ways to improve. Is it possible to make the code more readable, maintainable, readable, and scalable?</p>
</li>
<li><p><strong>Prioritize</strong>: Following your understanding and identification, you can now prioritize the areas that require your focus first. Make a plan to deal with them. Create an Impact Effort matrix (2x2) to understand the potential impact and effort required for each task.</p>
</li>
<li><p><strong>Backup</strong>: Even though version control systems are already used by the majority of engineering teams, it's a good idea to keep this in mind. Make sure your application is safely backed up in its most recent stable state before making any modifications. Both the data storage and the codebase.</p>
</li>
<li><p><strong>Test</strong>: To ensure that the refactoring has not produced any new bugs or unexpected behavior, validate it with automated tests. Write your tests against desired behavior. If there are existing tests, keep them to validate that the changes have not produced any side effects. The rule is simple: If you can test it, you can refactor it.</p>
</li>
<li><p><strong>Decompose</strong>: When broken up into small pieces, it is simpler to handle the refactoring process. You can isolate those parts and have the option to test those smaller components separately. Additionally, breaking things into tiny pieces makes it simpler to evaluate your impact. It's much simpler to understand and document the smaller part.</p>
</li>
<li><p><strong>Document</strong>: Start keeping track of the procedure. Document your understanding and any changes you make. Ensure consistency between your refactoring approach and the documentation.</p>
</li>
<li><p><strong>Repeat</strong>: Keep repeating the process until all necessary components of the application are improved. Refactoring a legacy app is not a straightforward, one-day task; it requires ongoing planning and execution.</p>
</li>
</ol>
<h2 id="heading-why-would-refactoring-even-be-necessary">Why would refactoring even be necessary?</h2>
<p>Let's imagine! You have a large service called <code>Printer</code> that has numerous dependencies. And it wasn't always this way. The first code was written ten years ago by two college friends. It was intended to print two simple tables. The developers felt there was no need to make it complicated. They decided that they will easily create a simple service to handle those tables. There were two methods in the service: <code>printEmployeesTable()</code> and <code>printDocumentsTable()</code>. Those methods simply converted tables to pdf format. As a result, the first dependency was a service called <code>HtmlToPdfConverter</code>. Fast forward to today, their product is a success story. They've been adding new features and appealing to larger audiences since then. As an outcome, more printing methods were required, such as <code>printSalariesTable()</code>, <code>printTranslationsTable()</code>, and so on.</p>
<p>However, it wasn't that simple. They had to expand the capabilities of the <code>Printer</code> service because their audience was larger.  Such as queues, printing in several formats, permission, and much more. You might also assume that the dependencies started growing at the same pace. When new developers joined the team, they just followed the proven procedures. They didn't want to break it. The <code>Printer</code> could print approximately 100 different outputs after ten years of development, each with a corresponding so-called "simple" printing method. The <code>Printer</code> service and the other components are tightly coupled. Even though that service still functions today, it now contains at least 10,000 lines of code and perhaps 20 or even more dependencies. Making changes is a headache because it is not readable anymore. All printing methods are all out of sync and operate in different ways. You cannot just decide to add CSV printing functionality to all tables. It's quite difficult to keep it stable and bug-free. However, this is not the end of it. Your clients want the same print functionality for a feature you're planning to release. The tricky part is that you need another dependency in order to print this new output. What will you do in this situation? Does it make sense to add a dependency and a new method to the <code>Printer</code>? When there are already more than 20 dependencies? Dr. Refactoring, we need your superpowers!</p>
<h2 id="heading-knowledge-to-the-rescue">Knowledge to the rescue</h2>
<p>As we already know, practically every other component highly depends on the <code>Print</code> service. How can we even take a small piece of it out without affecting the other components?</p>
<p>I'll give you a hint: There is a solution to this problem, and there will always be a solution. For this new feature, you should start with creating a test case. Once you have a failing test case, you can start the decomposition. The decorator pattern, for example, could be useful in that situation. You may make a standalone <code>NewFeaturePrinter</code> and decorate the good old <code>Printer</code> service. You may encapsulate the logic of the <code>NewFeaturePrinter</code> in itself this way. It can be tested in isolation. The old <code>Printer</code> doesn't even need to be changed, and it is unaware of the <code>NewFeaturePrinter</code>. At the very least, it's a much better alternative than adding more complexity to the already-existing <code>Printer</code>. The decorator pattern could also help you in breaking down this large service into smaller ones, more manageable pieces, especially when your components are tightly coupled. Perhaps you can resolve this issue without even using a pattern. The idea is that we should figure out a way to separate it into encapsulated chunks. This description of decomposition is also known as the Extract Method, a well-known refactoring technique.</p>
<p>There are many design patterns and several different techniques. Almost all of them were motivated by a specific problem. Increasing your knowledge could save you and your team a significant amount of time and money. Most businesses are unaware of their technical debt until it is too late. Refactoring should be an ongoing process for legacy applications.</p>
<h2 id="heading-final-thoughts-keep-it-simple">Final thoughts: Keep it simple</h2>
<p>Each team is organized differently. Finding one single way to manage this process for all is challenging. However, you can always follow the steps I defined above. Not everything needs to be refactored at once. Begin with renaming. Begin with breaking down complex services into simpler ones. Leverage design patterns. Learn from your own mistakes. Decouple and encapsulate your components. Test in isolation. Progressively get away from the complexity. And repeat.</p>
]]></content:encoded></item><item><title><![CDATA[Symfony is not the best framework.]]></title><description><![CDATA[Initially, I wanted to reply to a Reddit comment. It was already long enough to classify as a blog post. I hope you enjoy reading it. Let me know your thought on the comments.
Simply put, Symfony isn't ideal for every situation.
Who am I?
I'm Ozan Ak...]]></description><link>https://compiler.blog/symfony-is-not-the-best-framework</link><guid isPermaLink="true">https://compiler.blog/symfony-is-not-the-best-framework</guid><category><![CDATA[PHP]]></category><category><![CDATA[Symfony]]></category><category><![CDATA[Laravel]]></category><category><![CDATA[php frameworks]]></category><dc:creator><![CDATA[Ozan Akman]]></dc:creator><pubDate>Sat, 28 Jan 2023 23:01:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/gAe1pHGc6ms/upload/c34d515583f300f49e6154e8b458db5b.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Initially, I wanted to reply to a Reddit <a target="_blank" href="https://www.reddit.com/r/PHP/comments/10mbfuv/comment/j6665hq/?utm_source=share&amp;utm_medium=web2x&amp;context=3">comment</a>. It was already long enough to classify as a blog post. I hope you enjoy reading it. Let me know your thought on the comments.</p>
<p><strong>Simply put, Symfony isn't ideal for every situation.</strong></p>
<h2 id="heading-who-am-i">Who am I?</h2>
<p>I'm Ozan Akman, an experienced full-stack developer. I've worked with Laravel, Laminas (formerly Zend), and Symfony for many years. I've spent the last five years working for product companies. So, enterprise-level PHP frameworks were always on my radar. Due to my new employment, I shifted to backend development 1.5 years ago. Since that shift, Symfony is what I use. I love it. But must admit that I've seen some terrible Symfony apps. The developers who built a beautiful architecture that is enjoyable to work with are responsible for this "love it" experience, not the framework itself.</p>
<p>There are several requirements, but let's try to figure it out.</p>
<h2 id="heading-what-is-symfony-best-at">What is Symfony best at?</h2>
<ol>
<li><p>Development speed? Laravel is much faster than Symfony.</p>
</li>
<li><p>Overall development experience? When you get used to it and properly put it up, Symfony is pretty good.</p>
</li>
<li><p>Learning curve? Symfony is not the best choice.</p>
</li>
<li><p>Documentation? Symfony is effective but messy at the same time. It could be improved.</p>
</li>
<li><p>Configuration? Symfony is a fantastic choice.</p>
</li>
<li><p>Dealing with complexity? Symfony is one of my best experiences to solve complicated challenges with ease.</p>
</li>
</ol>
<p>In the end, it all comes down to your goals.</p>
<h2 id="heading-asking-the-right-questions">Asking the right questions</h2>
<p>Do you have limited resources and need to complete projects quickly? Laravel is a fantastic option. Do you prefer less magic and more stable ground? Symfony is a fantastic option. Will you be handling the client code? Laravel + Inertia is a great option. Designing an API only? Symfony is an amazing option. But it couldn't even come closer to Laravel's full-stack capabilities. Testing with both frameworks is a joy Do you really need so much flexibility? Laminas is great and flexible but it makes things harder and DX isn't the best. You deliver slower than other frameworks.</p>
<p>Every framework can scale. Each and every framework has excellent tooling. To be honest, working with nearly any advanced PHP framework is a joy. We have options, which is great.</p>
<h2 id="heading-my-personal-experience-with-frameworks">My personal experience with frameworks</h2>
<p>And again, I love Symfony. Two months ago, I started working on weekend projects, and Symfony was my first pick. After a month, I made the decision to try Laravel because I believed I could develop more quickly with it. That was true. I have a nice speed boost thanks to Laravel. You should know that I haven't worked with Laravel in about five years. There were a few tweets and stories that I saw, but that was it. For many years, I lacked first-hand experience. I could change to it so quickly. It was quite surprising. And the combination of Laravel and Inertia is simply amazing. Everything depends on the content and timing of your delivery.</p>
<p>I can also see why Laravel is so hated, especially among experienced developers. It has a feature called Facade that allows you to access objects from a container. I try to avoid using the Facade feature because it feels like too much magic. Of course, you are free to use dependency injection. But Laravel forces you to use its own methods, and you're not as remotely free as Symfony's flexibility. It lacked proper type hinting for so long. Unquestionably, there is room for improvement. Laravel isn't the best, but it does the job for a full-stack project with just a solo programmer.</p>
<h2 id="heading-whats-the-best-php-framework">What's the best PHP framework?</h2>
<p>I intentionally stated that "it is not the best framework" for all of the mentioned frameworks above because none of them is the best. You need to make the most of them.</p>
<p>You need to focus on creating your system using design patterns, having a good architecture (e.g. hexagonal architecture), and around DDD principles.</p>
<p>The next time you choose one of those frameworks, you need to focus on delivery and timing. You need to decide what's the best framework for you.</p>
<p>In the end, there is no such thing as "the best" framework. It all depends on you!</p>
]]></content:encoded></item><item><title><![CDATA[Understanding black-box and white-box testing]]></title><description><![CDATA[What is black-box testing?
Blackbox testing is a method of software testing that examines the functionality of an application without peering into its internal structures or workings. It is also known as functional testing or behavioral testing. The ...]]></description><link>https://compiler.blog/understanding-black-box-and-white-box-testing</link><guid isPermaLink="true">https://compiler.blog/understanding-black-box-and-white-box-testing</guid><category><![CDATA[Testing]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[Automated Testing]]></category><category><![CDATA[Programming Blogs]]></category><dc:creator><![CDATA[Ozan Akman]]></dc:creator><pubDate>Tue, 17 Jan 2023 10:04:22 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/w7ZyuGYNpRQ/upload/26478fc55095d396970553ac869f2ecc.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-what-is-black-box-testing">What is black-box testing?</h2>
<p>Blackbox testing is a method of software testing that examines the functionality of an application without peering into its internal structures or workings. It is also known as functional testing or behavioral testing. The tester is only concerned with what the application does, not how it does it. The goal of black-box testing is to ensure that the application meets its requirements and behaves as expected for the end user.</p>
<h2 id="heading-should-we-test-internal-parts-with-functional-test-cases">Should we test internal parts with functional test cases?</h2>
<p><strong>Functional testing</strong>, also known as black-box testing, is focused on testing the software application's external behavior and functionality, as seen from the end user's perspective. It is not typically used to test the internal workings or structure of the application.</p>
<p>Internal parts of an application, such as individual modules or components, are typically tested using <strong>unit testing</strong>, which is a method of testing individual pieces of code in isolation. Developers typically write unit tests to verify that the code they have written is working correctly and meets the requirements.</p>
<p>That being said, it is possible to have functional test cases that test the internal parts of the application by testing the interaction of different internal components, in this case, it's called <strong>integration testing</strong>. This method of testing ensures that the various parts of the application are working together correctly and that there are no conflicts or issues when they are integrated.</p>
<p>In summary, functional testing is focused on the external behavior and functionality of the application, while unit testing and integration testing are focused on the internal parts of the application.</p>
<h2 id="heading-what-are-the-key-differences-between-black-box-and-white-box-testing">What are the key differences between black-box and white-box testing?</h2>
<p>Black-box testers only have access to the inputs and outputs of the application, and not to the internal code. This method of testing is used to ensure that the application behaves as expected for the end user and meets its requirements.</p>
<p>On the other hand, white-box testers have access to the internal code and design of the application and use this information to test individual pieces of code to ensure that they are working correctly. This method of testing is used to ensure that the application is functioning correctly at the code level and to identify any bugs or issues that may not be visible from the user's perspective.</p>
<h3 id="heading-in-summary-the-key-differences">In summary, the key differences:</h3>
<ul>
<li><p>Black-box testing is done from the user's perspective, while white-box testing (unit, integration, etc.) is done from the developer's perspective.</p>
</li>
<li><p>Black-box testing does not require access to the internal code, while white-box testing requires access to the internal code.</p>
</li>
<li><p>Black-box testing is focused on testing the external functionality and behavior of the application, while white-box testing is focused on testing the internal code and design.</p>
</li>
</ul>
<p>Sources:</p>
<ul>
<li><p><a target="_blank" href="https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-192.pdf">https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-192.pdf</a></p>
</li>
<li><p><a target="_blank" href="https://csrc.nist.gov/glossary/term/black_box_testing">https://csrc.nist.gov/glossary/term/black_box_testing</a></p>
</li>
</ul>
]]></content:encoded></item></channel></rss>