<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Luka Peharda</title>
    <description>The latest articles on Forem by Luka Peharda (@lukapeharda).</description>
    <link>https://forem.com/lukapeharda</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F569676%2F2ad22b0c-037b-4717-a124-4a3d8cdfd28d.jpg</url>
      <title>Forem: Luka Peharda</title>
      <link>https://forem.com/lukapeharda</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/lukapeharda"/>
    <language>en</language>
    <item>
      <title>Do We Still Need NoSQL in 2026?</title>
      <dc:creator>Luka Peharda</dc:creator>
      <pubDate>Mon, 23 Feb 2026 07:06:17 +0000</pubDate>
      <link>https://forem.com/lukapeharda/do-we-still-need-nosql-in-2026-4p2m</link>
      <guid>https://forem.com/lukapeharda/do-we-still-need-nosql-in-2026-4p2m</guid>
      <description>&lt;p&gt;I graduated university in 2007. Back then, databases meant one thing: tables, rows, and relationships. A user has orders. Orders have products. Everything is connected, and you draw those connections carefully on a whiteboard (or a notebook) before you write a single line of code.&lt;/p&gt;

&lt;p&gt;That's just how data works, right? Things in the real world are related to each other. It makes sense to model that. Similar how classes in OOP are a natural way to model real-world entities, relational databases felt like the natural way to model real-world data.&lt;/p&gt;

&lt;p&gt;Then NoSQL came along. Suddenly, you could just throw data into a big flexible blob without worrying too much about structure or relationships. It was like someone had looked at a filing cabinet and said "what if we just used a pile on the floor instead?"&lt;/p&gt;

&lt;p&gt;Maybe that's my bias showing, or my old age. But I've been sitting with this question for a while now: &lt;strong&gt;do we actually need NoSQL databases in 2026, or have I just not updated my thinking since 2007?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let me try to work through it "on-paper" here.&lt;/p&gt;




&lt;h2&gt;
  
  
  What even is NoSQL?
&lt;/h2&gt;

&lt;p&gt;If you're not a developer, here's the short version. A traditional relational database (like PostgreSQL or MySQL) stores data in structured tables, kind of like spreadsheets, and lets you link those tables together. NoSQL is a term for databases that don't do that. They store data differently, often as flexible documents (imagine JSON files), key-value pairs (like a dictionary), or other formats.&lt;/p&gt;

&lt;p&gt;The pitch for NoSQL is roughly: more flexibility, easier to scale to massive amounts of data, no need to define your structure upfront. This "no need to define structure" was a big part of the appeal. Just throw data in and figure out how to use it later.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where NoSQL makes sense
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;When speed is everything and the data is simple.&lt;/strong&gt; Think of things like a leaderboard in a game, user session data, or a cache. You're not doing complex queries, you just need to store something and retrieve it incredibly fast. Key-value stores like Redis are brilliant at this. A relational database would be overkill.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When your data doesn't have a fixed shape.&lt;/strong&gt; Imagine a product catalog where a t-shirt has "size" and "color" attributes, but a laptop has "RAM", "CPU", and "screen size". These are very different. Forcing them into the same table structure is awkward. A document store like MongoDB lets each product just be what it is.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When you're dealing with enormous scale across multiple regions.&lt;/strong&gt; Big tech companies (think Amazon, Netflix, or Facebook) have data spread across data centers all over the world. Some NoSQL databases are specifically built to handle that kind of scale, accepting some trade-offs (like the data not being perfectly up-to-date everywhere at all times) to stay fast.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For highly connected data, like social networks.&lt;/strong&gt; Ironically, one area where NoSQL really shines is &lt;em&gt;relationships&lt;/em&gt;, but a specific kind. Graph databases (like Neo4j) are built to answer questions like "who are the friends of my friends who also like jazz?" across millions of people. A relational database can do this, but it gets slow fast.&lt;/p&gt;

&lt;p&gt;So yes, there are real use cases. But those use cases are pretty specific. They're not the kind of thing most apps need.&lt;/p&gt;




&lt;h2&gt;
  
  
  But here's where my head still hurts
&lt;/h2&gt;

&lt;p&gt;The thing is, most apps aren't Netflix. Most apps I've worked on, and most apps most developers build, have data that &lt;em&gt;is&lt;/em&gt; relational. Users have profiles. Profiles belong to organizations. Organizations have subscriptions. Subscriptions have invoices.&lt;/p&gt;

&lt;p&gt;That stuff has relationships. And relational databases were literally invented to model exactly that.&lt;/p&gt;

&lt;p&gt;When I look at a NoSQL document store handling that kind of data, I often see one of two things. Either the data ends up duplicated everywhere (the user's name stored in fifty different places), or the app code ends up doing the work that the database used to do - manually stitching data together, checking consistency, making sure nothing gets out of sync.&lt;/p&gt;

&lt;p&gt;That's not a win. That's moving complexity from a place designed to handle it (the database) to a place that's much worse at handling it (your application code, written by humans, at 11pm, day before Black Friday).&lt;/p&gt;




&lt;h2&gt;
  
  
  Meanwhile, relational databases haven't been standing still
&lt;/h2&gt;

&lt;p&gt;This is something I think gets missed in the NoSQL conversation. Tools like PostgreSQL have been quietly adding features that close the gap significantly.&lt;/p&gt;

&lt;p&gt;For example - PostgreSQL has a column type called &lt;code&gt;jsonb&lt;/code&gt; that lets you store flexible, document-like data &lt;em&gt;inside&lt;/em&gt; a relational database. So you can have the best of both worlds - structured, relational data where you need it, and flexible blobs where you don't. And all of that in one place, with proper transactions and data integrity guarantees.&lt;/p&gt;

&lt;p&gt;Modern relational databases can also handle a lot more scale than people give them credit for. The "NoSQL scales better" argument made a lot of sense in 2010. In 2026, it's a much more nuanced picture.&lt;/p&gt;




&lt;h2&gt;
  
  
  The honest downsides of NoSQL
&lt;/h2&gt;

&lt;p&gt;I want to be clear I'm not just being a grumpy old dev here. There are real trade-offs with NoSQL that don't always get talked about enough.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You lose consistency guarantees.&lt;/strong&gt; Many NoSQL databases are "eventually consistent," which means for a short time, different users might see different versions of the data. For some apps that's fine. For anything involving money or critical records, it can be a serious problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You become responsible for your data's integrity.&lt;/strong&gt; Relational databases enforce rules. For example, you can't link an order to a user that doesn't exist. With many NoSQL databases, that's your problem now. One bug in your code and you've got orphaned, inconsistent data with no safety net.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flexible schemas are a double-edged sword.&lt;/strong&gt; Yes, it's nice to not have to define everything upfront. But six months into a project, when your documents all look slightly different because three developers made different assumptions, you'll wish someone had drawn a schema on a whiteboard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Querying can get awkward.&lt;/strong&gt; SQL (despite its age) is a remarkably powerful and expressive way to ask questions of your data. Equivalent operations in some NoSQL databases require more code, more effort, and sometimes just aren't possible at all.&lt;/p&gt;




&lt;h2&gt;
  
  
  So where does that leave me?
&lt;/h2&gt;

&lt;p&gt;Honestly? Still pretty relational in my thinking. And I think I'm okay with that. At least for now, until I get a project that genuinely needs NoSQL's strengths.&lt;/p&gt;

&lt;p&gt;I don't think my instinct to model data as interconnected things is a limitation from my education. I think it reflects something true about how a lot of real-world data actually works. And relational databases are really good at modeling that.&lt;/p&gt;

&lt;p&gt;Does that mean I'd never use NoSQL? No. But I'd want to be very sure I needed it before I went down that path.&lt;/p&gt;

&lt;p&gt;My overall take: &lt;strong&gt;if you're building most apps, a well-used PostgreSQL database will take you further than you think, for longer than you think, with fewer surprises than you think.&lt;/strong&gt; And if you ever genuinely outgrow it, you'll know. And the NoSQL option will still be there and you'll be able to migrate your data if needed.&lt;/p&gt;

&lt;p&gt;I just wouldn't start there.&lt;/p&gt;

</description>
      <category>nosql</category>
      <category>mongodb</category>
      <category>postgressql</category>
    </item>
    <item>
      <title>Why I Hate Black Fridays: A Developer's Thurs-dread Manifesto</title>
      <dc:creator>Luka Peharda</dc:creator>
      <pubDate>Thu, 28 Nov 2024 10:00:13 +0000</pubDate>
      <link>https://forem.com/lukapeharda/why-i-hate-black-fridays-a-developers-thurs-dread-manifesto-48ep</link>
      <guid>https://forem.com/lukapeharda/why-i-hate-black-fridays-a-developers-thurs-dread-manifesto-48ep</guid>
      <description>&lt;p&gt;Let's get one thing straight: I'm not a discount-hating Grinch.&lt;/p&gt;

&lt;p&gt;As a consumer, I love a good bargain. As a developer, I'm all for spreading some financial joy through product discounts. But there's a special circle of software hell reserved for the Thursday before Black Friday – what I've christened "Thurs-dread" (get it? Thursday + dread? I'll wait while you appreciate my linguistic genius).&lt;/p&gt;

&lt;p&gt;Picture this: It's the day before retail's version of the Hunger Games, and I'm sitting at my keyboard, happily clacking on my keyboard introducing bugs into the codebase, bracing for the annual software circus of discount madness.&lt;/p&gt;

&lt;h2&gt;
  
  
  Survival Strategy #1: The Mythical Universal Checkout System
&lt;/h2&gt;

&lt;p&gt;Some naive souls might suggest building a checkout system so robust it could handle every possible discount scenario. Ha! If only.&lt;/p&gt;

&lt;p&gt;The moment you think you've created the software equivalent of a puzzle with no solution, the marketing team swoops in with ideas so wild they'd confuse a GPS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Survival Strategy #2: The Pre-Emptive Interrogation
&lt;/h2&gt;

&lt;p&gt;Asking the product team about their Black Friday plans a month in advance? That's cute. They'll look you dead in the eye and swear nothing special is happening. Cut to three weeks later – their inboxes are overflowing with promotional emails, and suddenly they've transformed into discount innovation machines.&lt;/p&gt;

&lt;p&gt;It's like watching a mild-mannered IT guy turn into a used car salesman overnight.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Coping Mechanisms: A Tragicomedy in Multiple Acts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Therapeutic Article
&lt;/h3&gt;

&lt;p&gt;Writing about my frustrations publicly. Sometimes, shouting into the void feels good. This very article serves as Exhibit A.&lt;/p&gt;

&lt;h3&gt;
  
  
  The "Yes, But" Diplomatic Dance
&lt;/h3&gt;

&lt;p&gt;The art of agreeing while meticulously documenting why their last-minute request is about as feasible as teaching a cat to juggle. Success rate? A solid 50%. Not great, not terrible.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Passive-Aggressive Emoji Ballet
&lt;/h3&gt;

&lt;p&gt;Responding with a barrage of cheerful emojis while internally crying. 🌈😄🎉&lt;/p&gt;




&lt;p&gt;Jokes aside, I work with an incredible team who actually listens and tries to meet me halfway.&lt;/p&gt;

&lt;p&gt;So, fellow developers and discount warriors: How do YOU survive the Thurs-dread?&lt;/p&gt;

</description>
      <category>blackfriday</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>When does the TDD approach make sense?</title>
      <dc:creator>Luka Peharda</dc:creator>
      <pubDate>Thu, 17 Oct 2024 07:29:01 +0000</pubDate>
      <link>https://forem.com/lukapeharda/when-does-the-tdd-approach-make-sense-439j</link>
      <guid>https://forem.com/lukapeharda/when-does-the-tdd-approach-make-sense-439j</guid>
      <description>&lt;p&gt;I don't like &lt;em&gt;writing&lt;/em&gt; tests. But I like &lt;em&gt;having&lt;/em&gt; tests. They give me a sense of security and confidence that my code works as expected.&lt;/p&gt;

&lt;p&gt;I never bought into TDD practices.&lt;/p&gt;

&lt;p&gt;I know the approach can be very useful and in the long run it can save you time and money. But there is always a time pressure when developing features and not enough time to write tests.&lt;/p&gt;

&lt;p&gt;But I always start with TDD when I need to develop a feature that will require me to test a lot of different variants and edge cases, and especially when there are a lot of clicking and moving parts and especially when external platforms and APIs are involved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Order::nextPaymentAmount method
&lt;/h2&gt;

&lt;p&gt;Let me give you an example from the project that I'm working on.&lt;/p&gt;

&lt;p&gt;I had to add a method &lt;strong&gt;nextPaymentAmount&lt;/strong&gt; to the &lt;strong&gt;Order&lt;/strong&gt; class which would return the amount of the next recurring payment.&lt;/p&gt;

&lt;p&gt;Payment can be a one-time, a subscription, or installment plan. It can have setup fees and trials. It can have discounts and coupons. Coupons can be applied only to the first payment or to all recurring payments as well. It can have taxes. It can have a lot of different scenarios.&lt;/p&gt;

&lt;p&gt;A lot of unnecessary clicking to test all of these combinations manually.&lt;/p&gt;

&lt;h2&gt;
  
  
  TDD to the rescue
&lt;/h2&gt;

&lt;p&gt;In scenarios like above, I always use TDD and it saves me incredible amounts of time.&lt;/p&gt;

&lt;p&gt;I'm not strictly adhering to TDD practices. I skip steps.&lt;/p&gt;

&lt;p&gt;First, I write down all test cases that I can think of.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NextPaymentAmountTest&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;TestCase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="mf"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testOneTimePaymentHasNoNextPayment&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testSubscriptionHasNextPayment&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testSubscriptionWithSetupFeeHasNextPaymentWithDifferentAmount&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testSubcriptionWithBasicCouponHasNextPaymentWithoutDiscount&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testSubscriptionWithRecurringCouponHasNextPaymentWithDiscount&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="mf"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I focus on a single case.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="mf"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testOneTimePaymentHasNoNextPayment&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Arrange&lt;/span&gt;
    &lt;span class="nv"&gt;$pricingPlan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PricingPlan&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s1"&gt;'type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'one-time'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'amount'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="nv"&gt;$order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s1"&gt;'pricing_plan_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$pricingPlan&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'member_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;member&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'membership_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;membership&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="c1"&gt;// Act&lt;/span&gt;
    &lt;span class="nv"&gt;$nextPaymentAmount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;nextPaymentAmount&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Assert&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$nextPaymentAmount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="mf"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first, test fails because the function that I'm testing (&lt;strong&gt;Order::nextPaymentAmount&lt;/strong&gt;) is not yet implemented. Then I implement it just so the test passes.&lt;/p&gt;

&lt;p&gt;Each test case is a small step towards the final implementation and adds a bit of code or conditional.&lt;/p&gt;

&lt;p&gt;Usually, as I go along these cases and write code to pass them, I find a lot more edge cases that need to be covered.&lt;/p&gt;




&lt;p&gt;Using this iterative approach I'm positive that I've covered most of the possible scenarios. And more importantly, I wasn't overwhelmed with "loading" all edge cases in my head before implementing the feature.&lt;/p&gt;

&lt;p&gt;TDD not only saved me a lot of time, but gave me confidence that the feature works as expected. And that it will work as we extend it in the future.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>tdd</category>
      <category>testing</category>
    </item>
    <item>
      <title>Code refactoring: what is it and why should you do it</title>
      <dc:creator>Luka Peharda</dc:creator>
      <pubDate>Thu, 28 Mar 2024 10:28:47 +0000</pubDate>
      <link>https://forem.com/lukapeharda/code-refactoring-what-is-it-and-why-should-you-do-it-1g3d</link>
      <guid>https://forem.com/lukapeharda/code-refactoring-what-is-it-and-why-should-you-do-it-1g3d</guid>
      <description>&lt;p&gt;There is a lot of talk and mention of refactoring in developer communities. But what is it exactly?&lt;/p&gt;

&lt;p&gt;Some use refactoring as an &lt;strong&gt;insult&lt;/strong&gt; - meaning your code is bad and it needs refactoring. Others use it as an &lt;strong&gt;excuse&lt;/strong&gt; - meaning my estimations were wrong because I needed to refactor legacy code or when regression bugs happen because they were “refactoring” code.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is refactoring
&lt;/h2&gt;

&lt;p&gt;Refactoring is the art of restructuring existing code without changing its external behavior. And this distinction without changing is really important.&lt;/p&gt;

&lt;p&gt;Refactoring shouldn't be done while you are adding new features to the code. First, refactor old code and then proceed with adding a new feature.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://refactoring.com/" rel="noopener noreferrer"&gt;Martin Fowler&lt;/a&gt; says that refactoring is a "series of small behavior-preserving transformations". And if he says so then who are you (or me) to question that 🫣&lt;/p&gt;

&lt;p&gt;He continues that "each transformation does little, but a sequence of these transformations can produce a significant restructuring."" By making small changes it is less likely you'll break things.&lt;/p&gt;

&lt;p&gt;Refactoring should not be a separate task that we'd add once in a while to our TO DO list. It should be a part of day-to-day programming.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why should you refactor your code?
&lt;/h2&gt;

&lt;p&gt;You should consider refactoring as a part of your code hygiene - similar to washing your teeth or washing your hands. You don't question why - you just do it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enhance readability and maintainability
&lt;/h3&gt;

&lt;p&gt;Easier to read code is easier to fix. Both by you and your colleagues. You know, code is read more than it's written so it is important to be easy on the eyes.&lt;br&gt;
The best way to learn the value of simple code is to be the person who has to pick it up again several months (or years) after it was written. Preferably in an emergency where you have to make and deploy a change very quickly, and also be confident that you're not going to take the system down.&lt;/p&gt;

&lt;h3&gt;
  
  
  Improve extensibility and maintainability
&lt;/h3&gt;

&lt;p&gt;Well-factored code is like Lego blocks. Easy to add a new feature to it, or to remove it if you don't need it anymore.&lt;/p&gt;

&lt;h3&gt;
  
  
  Increase performance
&lt;/h3&gt;

&lt;p&gt;Sometimes, by simplifying code, you inadvertently or intentionally fix an issue which improves performance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reduce technical debt
&lt;/h3&gt;

&lt;p&gt;Technical debt refers to accumulated code issues that make future development harder.&lt;/p&gt;

&lt;p&gt;Refactoring helps you pay down this debt by fixing issues like duplicate code, unused variables, and poor naming conventions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;However, refactoring shouldn't be done for its own sake.&lt;/strong&gt;&lt;br&gt;
Always have a clear goal in mind, such as improving readability, fixing a bug, or adding a new feature.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm giving a talk at the &lt;a href="https://weblica.hr/en/" rel="noopener noreferrer"&gt;Weblica conference&lt;/a&gt; about code refactoring so if you are interested in learning more, do come by. This will be my first talk at a conference (I'm not counting meetup talks) and I hope it will be both informative and amusing.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>refactoring</category>
    </item>
    <item>
      <title>Use EXISTS instead of COUNT &gt; 0 when checking if records exist</title>
      <dc:creator>Luka Peharda</dc:creator>
      <pubDate>Wed, 14 Feb 2024 10:07:00 +0000</pubDate>
      <link>https://forem.com/lukapeharda/use-exists-instead-of-count-0-when-checking-if-records-exist-a0k</link>
      <guid>https://forem.com/lukapeharda/use-exists-instead-of-count-0-when-checking-if-records-exist-a0k</guid>
      <description>&lt;p&gt;TL;DR Use &lt;code&gt;exists&lt;/code&gt; when querying if SQL records exists instead of using &lt;code&gt;count&lt;/code&gt;. &lt;code&gt;exists&lt;/code&gt; is much more efficient and breaks out of the loop when first record is found.&lt;/p&gt;




&lt;h2&gt;
  
  
  Using &lt;code&gt;count&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Until recently, when I had to check if a DB record that satisfies some conditions exists, I’ve used a &lt;code&gt;count&lt;/code&gt; and then check if returned result is greater than &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Plain SQL query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="nv"&gt;`post_likes`&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="nv"&gt;`member_id`&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="nv"&gt;`post_id`&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Extra tip: MySQL has `COUNT(&lt;/em&gt;)&lt;code&gt; optimised and it is faster and more efficient than using &lt;/code&gt;COUNT(id)` for example.*&lt;/p&gt;

&lt;p&gt;Laravel Eloquent query (using &lt;code&gt;postLikes&lt;/code&gt; relationship):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Did member like a post&lt;/span&gt;
&lt;span class="nv"&gt;$member&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;postLikes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Extra tip: notice the brackets after &lt;code&gt;postLikes()&lt;/code&gt; relationship name. This means we are using the relationship to generate query on the related table and setting up foreign key for us. If we used &lt;code&gt;$member-&amp;gt;postLikes-&amp;gt;count()&lt;/code&gt; , without the brackets, we would fetch all related records and then do a count afterwards. This would result in a much costlier DB query, and more memory used as all those records needs to saved to memory.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This would “force” a DB to count through all of the records that satisfies these conditions. And if your table is large enough it could take some time. Granted, probably in milliseconds but still it would do unnecessary work as it does not know that you are just interested in “existence” of the record and not the exact count.&lt;/p&gt;

&lt;p&gt;Of course, if you indexed the table “properly” and use a composite index on &lt;code&gt;member_id&lt;/code&gt; and &lt;code&gt;post_id&lt;/code&gt; columns result would be pretty fast in this scenario but in some others it still may be optimised.&lt;/p&gt;




&lt;h2&gt;
  
  
  Using &lt;code&gt;exists&lt;/code&gt; subquery
&lt;/h2&gt;

&lt;p&gt;Better way would be to use an &lt;code&gt;exists&lt;/code&gt; subquery. This is available in MySQL from version 5.7 so there is no reason not to use it.&lt;/p&gt;

&lt;p&gt;You can check MySQL docs on EXISTS &lt;a href="https://dev.mysql.com/doc/refman/8.0/en/exists-and-not-exists-subqueries.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;. There is also NOT EXISTS subquery if you want to query for inversion.&lt;/p&gt;

&lt;p&gt;EXISTS works by encapsulating a query in SELECT subquery:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="no"&gt;SELECT&lt;/span&gt; &lt;span class="nf"&gt;EXISTS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="no"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
    &lt;span class="no"&gt;FROM&lt;/span&gt; &lt;span class="sb"&gt;`post_likes`&lt;/span&gt;
    &lt;span class="no"&gt;WHERE&lt;/span&gt; &lt;span class="sb"&gt;`member_id`&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="sb"&gt;`post_id`&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Doesn’t matter if your SELECT fetches all columns (`&lt;/em&gt;&lt;code&gt;) or plain &lt;/code&gt;1` , SELECT will be discarded in EXISTS query.*&lt;/p&gt;

&lt;p&gt;This query will return &lt;code&gt;true&lt;/code&gt; if subquery has at least one record or &lt;code&gt;false&lt;/code&gt; if there are no records that satisfies your conditions. MySQL will break out of the “loop” when it finds the first record and this is what makes it more performant than the COUNT.&lt;/p&gt;

&lt;p&gt;In Laravel you can use &lt;code&gt;exists&lt;/code&gt; method on the query builder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Did member like a post&lt;/span&gt;
&lt;span class="nv"&gt;$member&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;postLikes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Eloquent will encapsulate the query in the EXISTS subquery.&lt;/p&gt;

&lt;p&gt;Eloquent also provides &lt;code&gt;whereExists&lt;/code&gt; , &lt;code&gt;whereNotExists&lt;/code&gt; , &lt;code&gt;doesntExist&lt;/code&gt;, &lt;code&gt;withExists&lt;/code&gt; and several more to allow you to build a query that you need.&lt;/p&gt;




&lt;h2&gt;
  
  
  Proper example of using &lt;code&gt;exists&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;I’ve been using this to check existence of all kinds of records and relationships. Like permissions, likes and even as a nested subquery.&lt;/p&gt;

&lt;p&gt;For example, when fetching a list of posts to display on the page, I want to know if member did like a post in order to show a proper UI icon. This could lead to a N+1 situation where for each post we’d have to do a separate SQL query to check if record exits.&lt;/p&gt;

&lt;p&gt;Or we can use EXISTS subquery:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="no"&gt;SELECT&lt;/span&gt; &lt;span class="sb"&gt;`id`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sb"&gt;`title`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sb"&gt;`content`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="no"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
    &lt;span class="no"&gt;FROM&lt;/span&gt; &lt;span class="sb"&gt;`post_likes`&lt;/span&gt;
    &lt;span class="no"&gt;WHERE&lt;/span&gt; &lt;span class="sb"&gt;`posts`&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="sb"&gt;`id`&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sb"&gt;`post_likes`&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="sb"&gt;`post_id`&lt;/span&gt; 
    &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="sb"&gt;`member_id`&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="sb"&gt;`is_liked`&lt;/span&gt;
&lt;span class="no"&gt;FROM&lt;/span&gt; &lt;span class="sb"&gt;`posts`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will be done in a single optimised SQL query and provide information if member liked a post in a generated &lt;code&gt;is_liked&lt;/code&gt; column. To be precise, MySQL will do N+1 subqueries to check for existence but this will be optimised and done internally.&lt;/p&gt;

&lt;p&gt;In Laravel you’d use &lt;code&gt;withExists&lt;/code&gt; to do the same thing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'title'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'content'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withExists&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'postLikes as is_liked'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$member&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'member_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$member&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;is_liked&lt;/code&gt; will be added as an attribute on each &lt;code&gt;$post&lt;/code&gt; model.&lt;/p&gt;




&lt;p&gt;This optimisation may not seem as important and it may look like a “micro” improvement. But if your tables have millions of records then you already know that every millisecond counts.&lt;/p&gt;

</description>
      <category>sql</category>
      <category>laravel</category>
      <category>db</category>
      <category>optimisation</category>
    </item>
    <item>
      <title>Using Tailwind CSS classes in markdown</title>
      <dc:creator>Luka Peharda</dc:creator>
      <pubDate>Wed, 01 Nov 2023 07:10:34 +0000</pubDate>
      <link>https://forem.com/lukapeharda/using-tailwind-css-classes-in-markdown-357i</link>
      <guid>https://forem.com/lukapeharda/using-tailwind-css-classes-in-markdown-357i</guid>
      <description>&lt;p&gt;One of the biggest reasons why I moved my blog from Gatsby to Astro was its markdown integration and ease of creating new articles using markdown syntax.&lt;/p&gt;

&lt;p&gt;Not only markdown but &lt;a href="https://mdxjs.com/" rel="noopener noreferrer"&gt;MDX&lt;/a&gt; - using JSX and component imports in markdown content.&lt;/p&gt;

&lt;p&gt;Using Tailwind CSS typography plugin&lt;br&gt;
As my blog is built using Tailwind CSS, content for the blog articles were also using Tailwind’s classes to style it. This means that instead of using regular&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;p&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;tags I’m using&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;p class="pb-3 text-slate-400 text-xl"&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to create a “simple” paragraph. As you can see this isn’t really simple nor does it enable me to write articles fast.&lt;/p&gt;

&lt;p&gt;Not that the speed of converting my thoughts and ideas into HTML markup was my biggest problem.&lt;/p&gt;




&lt;p&gt;Sensible person would use Tailwind’s typography plugin to style all content markup using CSS classes on the markdown content parent element. Their page even says that the plugin’S intended use is to to add beautiful typographic defaults to any vanilla HTML you don’t control, like HTML rendered from Markdown.&lt;/p&gt;

&lt;p&gt;How would the paragraph look like using Tailwind’s prose classes?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;article class="prose prose-p:pb-3 prose-p:text-slate-400 prose-p:text-xl"&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’d have to add modifiers to the parent element class attribute. And repeat those modifiers for all other “target” like a, h1, h2, ul and all others.&lt;/p&gt;

&lt;p&gt;And you’d have to reset some of the defaults that typography plugin brings to the table. It looks less and less as a sensible solution. Right?&lt;/p&gt;

&lt;p&gt;Assigning Custom Components to HTML elements&lt;br&gt;
Fortunately smart folks from MDX were already thinking of scenarios like that.&lt;/p&gt;

&lt;p&gt;That is why you can import your custom component and then export a components object that maps the standard HTML to your custom component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Paragraph from '../components/Paragraph.astro';
export const components = {p: Paragraph}

This will be styled custom paragraph.
Paragraph.astro component is pretty simple:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;p class="pb-3 text-slate-400 text-xl"&amp;gt;
    &amp;lt;slot /&amp;gt;
&amp;lt;/p&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt; is where the “child” content (paragraph’s text) will go.&lt;/p&gt;




&lt;p&gt;This whole article was written using standard markdown syntax with styling applied by overwriting the HTML with custom components. I must say that the speed of writing this article surprised even me. Whole article was written in VS Code without any need for custom HTML markup nor CSS nor messy copy/pasting from older articles.&lt;/p&gt;

&lt;p&gt;Now I’m off to find a good VS Code extension for Grammarly so I literally don’t have to use any other tool to publish an article.&lt;/p&gt;




&lt;p&gt;To learn more, visit Astro’s &lt;a href="https://docs.astro.build/en/guides/markdown-content/#assigning-custom-components-to-html-elements" rel="noopener noreferrer"&gt;Markdown Content&lt;/a&gt; guide or &lt;a href="https://mdxjs.com/table-of-components/" rel="noopener noreferrer"&gt;MDX Website&lt;/a&gt; for a full list of HTML elements that can be overwritten as custom components.&lt;/p&gt;

</description>
      <category>tailwindcss</category>
      <category>markdown</category>
      <category>mdx</category>
      <category>astro</category>
    </item>
    <item>
      <title>100 lines of code per file</title>
      <dc:creator>Luka Peharda</dc:creator>
      <pubDate>Wed, 01 Nov 2023 07:05:31 +0000</pubDate>
      <link>https://forem.com/lukapeharda/100-lines-of-code-per-file-2me9</link>
      <guid>https://forem.com/lukapeharda/100-lines-of-code-per-file-2me9</guid>
      <description>&lt;p&gt;It has gotten to my attention that one of the biggest dev agencies in the region has a pull-request requirement that checks whether each file committed is under 100 lines long.&lt;/p&gt;

&lt;p&gt;It seems like a pretty stupid requirement, right?&lt;/p&gt;

&lt;p&gt;Then I tried it.&lt;/p&gt;

&lt;p&gt;I had a class that was 133 lines long and I want to see whether I can get it under 100. As the class was already “doing one thing” it didn’t make sense to refactor it and split it into two files.&lt;/p&gt;

&lt;p&gt;First to go were the comments 😀 Method docblocks were replaced with proper type declarations and return types. To preserve the readability, variable and method names had to be changed to be self-explanatory although a bit longer. Conditionals were extracted into separate methods.&lt;/p&gt;

&lt;p&gt;Multiple simple IFs were combined into one, which did hurt readability a bit but it saved 5 lines 😀&lt;/p&gt;

&lt;p&gt;Somewhat duplicated code was extracted and parametrized which allowed me to reuse methods in several locations.&lt;/p&gt;

&lt;p&gt;And I got to 99 lines 🚀&lt;/p&gt;

&lt;p&gt;I was skeptical about this requirement when I started but now I see its benefits. It makes you think about your code a bit more and forces you to make it more readable while also removing clutter from your files. And as strange as this may sound, it was fun doing it 😆&lt;/p&gt;

</description>
    </item>
    <item>
      <title>100 lines of code per file</title>
      <dc:creator>Luka Peharda</dc:creator>
      <pubDate>Tue, 25 Oct 2022 07:39:26 +0000</pubDate>
      <link>https://forem.com/lukapeharda/100-lines-of-code-per-file-47bi</link>
      <guid>https://forem.com/lukapeharda/100-lines-of-code-per-file-47bi</guid>
      <description>&lt;p&gt;It has gotten to my attention that one of the biggest dev agencies in the region has a pull-request requirement that checks whether each file committed is under 100 lines long.&lt;/p&gt;

&lt;p&gt;It seems like a pretty stupid requirement, right?&lt;/p&gt;

&lt;p&gt;Then I tried it.&lt;/p&gt;

&lt;p&gt;I had a class that was 133 lines long and I want to see whether I can get it under 100. As the class was already “doing one thing” it didn’t make sense to refactor it and split it into two files.&lt;/p&gt;

&lt;p&gt;First to go were the comments 😀 Method docblocks were replaced with proper type declarations and return types. To preserve the readability, variable and method names had to be changed to be self-explanatory although a bit longer. Conditionals were extracted into separate methods.&lt;/p&gt;

&lt;p&gt;Multiple simple IFs were combined into one, which did hurt readability a bit but it saved 5 lines 😀&lt;/p&gt;

&lt;p&gt;Somewhat duplicated code was extracted and parametrized which allowed me to reuse methods in several locations.&lt;/p&gt;

&lt;p&gt;And I got to 99 lines 🚀&lt;/p&gt;

&lt;p&gt;I was skeptical about this requirement when I started but now I see its benefits. It makes you think about your code a bit more and forces you to make it more readable while also removing clutter from your files. And as strange as this may sound, it was fun doing it 😆&lt;/p&gt;

</description>
      <category>codereview</category>
      <category>coderefactoring</category>
      <category>programming</category>
    </item>
    <item>
      <title>Unexpected benefits of estimations</title>
      <dc:creator>Luka Peharda</dc:creator>
      <pubDate>Tue, 31 May 2022 08:42:03 +0000</pubDate>
      <link>https://forem.com/lukapeharda/unexpected-benefits-of-estimations-5085</link>
      <guid>https://forem.com/lukapeharda/unexpected-benefits-of-estimations-5085</guid>
      <description>&lt;p&gt;I yet have to meet a developer that likes doing estimations. And don't get me started on sticking with them.&lt;/p&gt;

&lt;p&gt;I get that they are necessary for management to make plans but that does not mean I have to like them or enjoy doing them. No matter how hard I try my estimations are never correct.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I've been using the &lt;a href="https://www.fool.com/the-ascent/small-business/project-management/articles/three-point-estimating/" rel="noopener noreferrer"&gt;three-point estimation&lt;/a&gt; technique lately.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Did you know you can use the estimation to jump-start and beat procrastination? No? Read on. You did? Well, why didn't you share it with me?&lt;/p&gt;

&lt;h2&gt;
  
  
  What is procrastination?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;"&lt;a href="https://www.verywellmind.com/the-psychology-of-procrastination-2795944" rel="noopener noreferrer"&gt;Procrastination&lt;/a&gt; is the act of delaying or putting off tasks until the last minute, or past their deadline."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Why? I'll leave that up to you as each of us have a large number of different reasons for it.&lt;/p&gt;

&lt;p&gt;In some cases, reasons for the procrastination can be because we don't know how to do something or even what needs to be done. Or we are intimidated by the size of the task in front of us. And this is where estimation kicks in!&lt;/p&gt;

&lt;h2&gt;
  
  
  How estimation can help?
&lt;/h2&gt;

&lt;p&gt;Well, when doing an estimation you'll have to get all information on what it is that you are building. You'll have to fill all missing gaps and deal with all ambiguous requirements.&lt;/p&gt;

&lt;p&gt;You'll split a large task into smaller manageable pieces. And you'll be able to procrastinate over a large number of smaller tasks.&lt;/p&gt;

&lt;p&gt;By doing that, the task will be manageable and our motivation will slightly improve.&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional benefits
&lt;/h2&gt;

&lt;p&gt;Usually, the best cure for procrastination is to just start working. Doing as small a chunk of work as possible. Guess what? By doing the estimation you've done exactly that! You've started working on your task. And now nobody can stop you.&lt;/p&gt;

&lt;p&gt;Not only that, but by providing the estimation number you'll be "going public" with it and now a much larger audience could "hold you accountable".&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article is a part of my personal pledge to write at least one article per month for a whole year. As you can see, I've written this article on the last day of May which means that I'm struggling with procrastination as well so if you know any good tips please let me know!&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to generate a full Tailwind CSS palette from a single color</title>
      <dc:creator>Luka Peharda</dc:creator>
      <pubDate>Wed, 27 Apr 2022 06:07:55 +0000</pubDate>
      <link>https://forem.com/lukapeharda/how-to-generate-a-full-tailwind-css-palette-from-a-single-color-1mb1</link>
      <guid>https://forem.com/lukapeharda/how-to-generate-a-full-tailwind-css-palette-from-a-single-color-1mb1</guid>
      <description>&lt;p&gt;You have an app where you allow users to select their brand color in order to style their website. Naturally, you use different shades of the color in your design - in some places you use ligher ones, and in other darker ones.&lt;/p&gt;

&lt;p&gt;There are a lot of color generators and app that generate Tailwind CSS color palette for you. Here are some of my favorites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://uicolors.app/create" rel="noopener noreferrer"&gt;Create Tailwind CSS color families&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://colorbox.io/" rel="noopener noreferrer"&gt;ColorBox&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tailwindcss.com/docs/customizing-colors#generating-colors" rel="noopener noreferrer"&gt;Generating Colors (from Tailwind CSS docs)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Problem is, you want to have a simple UI and the thought of forcing users to select all possible shades gives you a headache.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution 1. "Opacity"
&lt;/h2&gt;

&lt;p&gt;Official &lt;a href="https://www.youtube.com/watch?v=MAtaT8BZEAo" rel="noopener noreferrer"&gt;theming video from Tailwind CSS&lt;/a&gt; suggests adding a single (brand) color and then modify opacity (either text opacity or background opacity) to have lighter shades of your color.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-brand text-opacity-75"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Howdy&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Under the hood Tailwind CSS uses CSS variables to modify alpha channel of the color (A in RGBA color model) which is pretty smart. Please watch the above YouTube video to see more detailed explanation but the gist of it is this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.text-brand&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;--tw-text-opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;--tw-text-opacity&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is fine and dandy but what to do when you want to have darker colors as well? You could start with a darker color and have a greater range. Can you tell your users to select "darker" color? No. You can't. You shouldn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution 2. "HSL"
&lt;/h2&gt;

&lt;p&gt;For this reason I've been playing with HSL color model (where H stands for hue, S for saturation and L for lightness). By changing the L values (lightness) of a base color I was able to get OK looking palette for the whole range - both lighter and darker.&lt;/p&gt;

&lt;p&gt;I've written a PHP package called &lt;a href="https://github.com/lukapeharda/tailwindcss-color-palette-generator" rel="noopener noreferrer"&gt;Tailwind CSS Color Palette Generator&lt;/a&gt; to use exactly this to generate a full palette.&lt;/p&gt;

&lt;p&gt;It is easy to generate a palette by specifying your base color:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;LukaPeharda\TailwindCssColorPaletteGenerator\Color&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;LukaPeharda\TailwindCssColorPaletteGenerator\PaletteGenerator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Create base color from hex&lt;/span&gt;
&lt;span class="nv"&gt;$baseColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Color&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fromHex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'#ffff00'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// or from RGB&lt;/span&gt;
&lt;span class="nv"&gt;$baseColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Color&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fromRgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// or from HSL&lt;/span&gt;
&lt;span class="nv"&gt;$baseColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Color&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fromHsl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Generate a palette&lt;/span&gt;
&lt;span class="nv"&gt;$paletteGenerator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PaletteGenerator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$paletteGenerator&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setBaseColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$baseColor&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$palette&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$paletteGenerator&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getPalette&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generated &lt;code&gt;$palette&lt;/code&gt; will be an array where keys are Tailwind CSS color steps and values &lt;code&gt;Color&lt;/code&gt; objects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$palette&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;300&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mf"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can then loop over it to generate CSS variables or use it anyway you see fit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$palette&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$key&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$color&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'--color-brand-'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$key&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;': #'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$color&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getHex&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;';'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Extend color settings in your &lt;code&gt;tailwind.config.js&lt;/code&gt; file and add &lt;code&gt;brand&lt;/code&gt; color palette:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;brand&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;var(--color-brand-50, #F5F3FF)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;var(--color-brand-100, #EDE9FE)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;var(--color-brand-200, #DDD6FE)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;var(--color-brand-300, #C4B5FD)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;var(--color-brand-400, #A78BFA)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;var(--color-brand-500, #8B5CF6)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;var(--color-brand-600, #7C3AED)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="mi"&gt;700&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;var(--color-brand-700, #6D28D9)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;var(--color-brand-800, #5B21B6)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="mi"&gt;900&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;var(--color-brand-900, #4C1D95)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Afterwards you can use your color as regular CSS Tailwind class, for example as &lt;code&gt;text-brand-100&lt;/code&gt; or &lt;code&gt;bg-brand-300&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Documentation and settings for fine tunning can be found &lt;a href="https://github.com/lukapeharda/tailwindcss-color-palette-generator" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Generated palettes are not as great as the ones from the official Tailwind CSS color palettes but hey, those were carefully hand picked.&lt;/p&gt;

</description>
      <category>tailwindcss</category>
      <category>php</category>
    </item>
    <item>
      <title>Why wrapping 3rd-party calls to external services is always a good idea</title>
      <dc:creator>Luka Peharda</dc:creator>
      <pubDate>Thu, 31 Mar 2022 13:24:38 +0000</pubDate>
      <link>https://forem.com/lukapeharda/why-wrapping-3rd-party-calls-to-external-services-is-always-a-good-idea-46ae</link>
      <guid>https://forem.com/lukapeharda/why-wrapping-3rd-party-calls-to-external-services-is-always-a-good-idea-46ae</guid>
      <description>&lt;p&gt;When using a 3rd-party package that makes calls (or API requests) to an external service it is always a good idea to wrap 3rd-party service with a thin wrapper (Facade). Why?&lt;/p&gt;

&lt;p&gt;Wrapping it avoids vendor lock-in and tight coupling. Imagine that the API the 3rd-party is using stops working. Now you need to change service calls across your whole project. If wrapped, you only need to modify your code once.&lt;/p&gt;

&lt;p&gt;Another reason is that you can then build and setup the 3rd-party service (or the client) according to your requirements.&lt;/p&gt;

&lt;p&gt;Let me give you an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="mf"&gt;...&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Kourses\Website&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Mpociot\VatCalculator\VatCalculator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="mf"&gt;...&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;VatHelper&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$calculator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Website&lt;/span&gt; &lt;span class="nv"&gt;$website&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$calculator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;VatCalculator&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$calculator&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setBusinessCountryCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$website&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;calculator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$calculator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="mf"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my project, I'm using &lt;code&gt;driesvints/vat-calculator&lt;/code&gt; package to calculate VAT (value-added tax) rate and to validate the EU VAT number. EU VAT number validation is being done by calling an external API.&lt;/p&gt;

&lt;p&gt;As VAT rate calculation needs to take into account your business location, I need to provide the business country code when setting up the calculator. As a product I'm working on is a SaaS, I need to specify this param on each request and can't do it through configuration files (which this package supports). So I'm "building" up the &lt;code&gt;VatCalculator&lt;/code&gt; according to my requirements.&lt;/p&gt;

&lt;p&gt;If I did not wrap this service, I would have to configure it each time I call it. Not a big deal I know, but this is a simple example.&lt;/p&gt;

&lt;p&gt;Besides doing a simple "wrapping" I've added a couple of methods that slightly modify 3rd-party service behavior.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="mf"&gt;...&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;KoursesMember&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;KoursesErrorsVatValidationException&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;VatHelper&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="mf"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;isValidVatId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$vatNumber&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;calculator&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;isValidVATNumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$vatNumber&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="nv"&gt;$exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;VatValidationException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"VAT validation failed."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getTaxRate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$countryCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$postalCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$vatNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$isCompany&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;isValidVatId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$vatNumber&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;calculator&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getTaxRateForLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$countryCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$postalCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$isCompany&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getTaxRateForMember&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Member&lt;/span&gt; &lt;span class="nv"&gt;$member&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getTaxRate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$member&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$member&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$member&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;vat_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In method &lt;code&gt;isValidVatId&lt;/code&gt; I'm catching a 3rd-party service exception and re-throwing it using a message (and code) that is in sync with my app. Now I don't need to catch 3rd-party package exceptions in my controllers.&lt;/p&gt;

&lt;p&gt;If I decide to change this 3rd-party package I wouldn't have to go through my code and change all mentions of this package.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;getTaxRate&lt;/code&gt; method, I've also slightly altered behavior which suits my flow better. I'm validating the VAT number before getting the tax rate for the user's location (as it is dependent on your business location as well as your customers' location).&lt;/p&gt;

&lt;p&gt;Method &lt;code&gt;getTaxRateForMember&lt;/code&gt; is a simple helper which allows me to easily pass my &lt;code&gt;$member&lt;/code&gt; object and get its current VAT rate without needing to "spread" required params.&lt;/p&gt;

&lt;p&gt;Using this technique you could easily implement caching in front of external service calls if need be. Here, as the VAT number checked should be different each time I'm not doing that.&lt;/p&gt;

&lt;p&gt;This wrapper borrows from a Facade design pattern, a Proxy, and even a Decorator. If you wish to learn more about design patterns be sure to check out &lt;a href="https://refactoring.guru/design-patterns" rel="noopener noreferrer"&gt;https://refactoring.guru/design-patterns&lt;/a&gt; as they have a nice overview of basic patterns as well as real-world examples.&lt;/p&gt;

</description>
      <category>php</category>
      <category>wrapper</category>
    </item>
    <item>
      <title>How to Refactor a Method With Optional Params</title>
      <dc:creator>Luka Peharda</dc:creator>
      <pubDate>Sat, 26 Feb 2022 07:44:08 +0000</pubDate>
      <link>https://forem.com/lukapeharda/how-to-refactor-a-method-with-optional-params-3n3c</link>
      <guid>https://forem.com/lukapeharda/how-to-refactor-a-method-with-optional-params-3n3c</guid>
      <description>&lt;p&gt;While refactoring and optimising legacy code I've ran into a method signature (optional parameters) inconsistency which caused performance issues as part of the expensive operation was being done without developer intention.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PageRepository&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="mf"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pageId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$prerender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="mf"&gt;...&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$prerender&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$page&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;prerender&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="mf"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$page&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;findOrFail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pageId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$prerender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pageId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$prerender&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$page&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Page &lt;/span&gt;&lt;span class="nv"&gt;$pageId&lt;/span&gt;&lt;span class="s2"&gt; not found."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$page&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="mf"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our &lt;code&gt;PageRepository::find&lt;/code&gt; method had an optional parameter called &lt;code&gt;$prerender&lt;/code&gt; which was set to &lt;code&gt;false&lt;/code&gt; by default. The problem is that the method &lt;code&gt;PageRepository::findOrFail&lt;/code&gt; has the same optional parameter but its default value set to &lt;code&gt;true&lt;/code&gt;! &lt;/p&gt;

&lt;p&gt;If you're not careful reading method signature and default param values you can easily make a mistake and "prerender" a page (which is an expensive operation) without wanting or needing it.&lt;/p&gt;

&lt;p&gt;Methods with optional "flag" parameters can potentially be dangerous and can be hard to glance at.&lt;/p&gt;

&lt;p&gt;There are a couple of things that can be done here in order for it to be better and more readable.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Change optional parameter default values to the same value
&lt;/h2&gt;

&lt;p&gt;This one is a no brainer. Just change the &lt;code&gt;PageRepository::findOrFail&lt;/code&gt; optional &lt;code&gt;$prerender&lt;/code&gt; default value to &lt;code&gt;false&lt;/code&gt; (or vice-versa). &lt;/p&gt;

&lt;h2&gt;
  
  
  2. Specify optional param value when calling methods that have them
&lt;/h2&gt;

&lt;p&gt;Just specify a value explicitly so anyone reading your code (even you a couple of months in the future) will know that you've set it and used it intentionally.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$pageRepository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PageRepository&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nv"&gt;$page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$pageRepository&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pageId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or even better, use the variable with a semantic meaning when calling the method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$pageRepository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PageRepository&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nv"&gt;$page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$pageRepository&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pageId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$prerender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;I know that some linters or IDEs may highlight the &lt;code&gt;$prerender&lt;/code&gt; param but there are several ways to deal with it and I'm leaving it at your discretion to chose a way how to handle it.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Refactor the method with optional "flag" parameter into two distinct methods
&lt;/h2&gt;

&lt;p&gt;Remove the &lt;code&gt;$page-&amp;gt;prerender()&lt;/code&gt; call from the &lt;code&gt;PageRepository::find&lt;/code&gt; method and extract it to a separate &lt;code&gt;PageRepository::findAndPrerender&lt;/code&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PageRepository&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="mf"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pageId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$prerender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="mf"&gt;...&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$prerender&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$page&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;prerender&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="mf"&gt;...&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$page&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;findAndPrerender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pageId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pageId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$prerender&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$page&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Page &lt;/span&gt;&lt;span class="nv"&gt;$pageId&lt;/span&gt;&lt;span class="s2"&gt; not found."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$page&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="mf"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way you have to make an intentional effort to chose which functionality (or rather which side-effect) you require.&lt;/p&gt;

&lt;p&gt;This was just a quick refactoring tip which I’m usually enforcing suggesting while doing code reviews in order to make code easily understandable and to avoid confusion.&lt;/p&gt;

</description>
      <category>refactoring</category>
      <category>programming</category>
      <category>php</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
