<?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>DEV Community: Luka Peharda</title>
    <description>The latest articles on DEV Community by Luka Peharda (@lukapeharda).</description>
    <link>https://dev.to/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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F569676%2F2ad22b0c-037b-4717-a124-4a3d8cdfd28d.jpg</url>
      <title>DEV Community: Luka Peharda</title>
      <link>https://dev.to/lukapeharda</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lukapeharda"/>
    <language>en</language>
    <item>
      <title>How to write better</title>
      <dc:creator>Luka Peharda</dc:creator>
      <pubDate>Wed, 17 Jun 2026 10:00:29 +0000</pubDate>
      <link>https://dev.to/lukapeharda/how-to-write-better-957</link>
      <guid>https://dev.to/lukapeharda/how-to-write-better-957</guid>
      <description>&lt;p&gt;I don't consider myself a great writer. But I do consider myself a writer who cares. And that might be even more important.&lt;/p&gt;

&lt;p&gt;I've been writing articles for a while now. Some are good. Some could be better. Some I'm proud of, most I'm ashamed of. But the articles that resonate most with readers aren't the ones with the fanciest words or the most perfect grammar. They're the ones that provide the most value.&lt;/p&gt;

&lt;p&gt;And that's really the whole point, isn't it?&lt;/p&gt;




&lt;h2&gt;
  
  
  Value over everything
&lt;/h2&gt;

&lt;p&gt;In order for your writing to improve, it needs to be valuable. And in order to be valuable to your readers, you need to know your readers.&lt;/p&gt;

&lt;p&gt;Sure, it needs to be clearly written. Organized. And you need to appear knowledgeable and persuasive. But if it does not have any value for your readers, they won't be willing to read it.&lt;/p&gt;

&lt;p&gt;What does that mean, to know your readers? You need to imagine who they are. Are they experts? Novices? Your friends? Your colleagues? Are they senior developers with 15 years of experience, or junior devs just starting their first job?&lt;/p&gt;

&lt;p&gt;I write primarily for developers like me. People who care about building good software, who want to learn, who appreciate a practical example over a theoretical lecture. And that shapes everything I write.&lt;/p&gt;




&lt;h2&gt;
  
  
  Your work does not have to be original
&lt;/h2&gt;

&lt;p&gt;You don't have to be the first person to ever write about a topic. You don't have to invent a new programming paradigm or discover a revolutionary concept. Most of the best articles I've read aren't groundbreaking, they're just really good explanations of existing ideas. Or just written in a way that resonates with me better.&lt;/p&gt;

&lt;p&gt;What you do need to do is provide value. Your writing needs to change the way your readers think about a problem. It needs to help them see something differently, understand something more deeply, or apply something more effectively.&lt;/p&gt;

&lt;p&gt;Think of it this way: you're not a researcher discovering new truths. You're a guide helping others navigate truths that already exist.&lt;/p&gt;




&lt;h2&gt;
  
  
  Write to change minds, not to explain
&lt;/h2&gt;

&lt;p&gt;In school, we're taught to write to explain. We're taught to write so that our teachers can see if we understood the matter. We're not taught to provide value to our teachers. Or to our classmates. Or to anyone else, really.&lt;/p&gt;

&lt;p&gt;But that's not how good writing works. Good writing isn't about proving you understand something. It's about helping someone else understand something.&lt;/p&gt;

&lt;p&gt;So don't write to explain. Write to change or shift perspectives. Write to make your reader think "Oh, I never thought of it that way before."&lt;/p&gt;




&lt;h2&gt;
  
  
  The structure that works
&lt;/h2&gt;

&lt;p&gt;A simple structure that works for me is this:&lt;/p&gt;

&lt;h3&gt;
  
  
  Open with a problem
&lt;/h3&gt;

&lt;p&gt;Start with something your reader is struggling with. A pain point. A frustration. Something that keeps them up at night (or at least makes their workday more annoying).&lt;/p&gt;

&lt;h3&gt;
  
  
  Then move to the solution
&lt;/h3&gt;

&lt;p&gt;Not just any solution. Your solution. Your approach. Your hard-won wisdom. The thing you wish someone had told you when you were facing that same problem.&lt;/p&gt;

&lt;p&gt;That's it. Problem, solution. It's simple, but it's effective.&lt;/p&gt;

&lt;p&gt;You don't need a fancy introduction. You don't need to define every term. You don't need to provide a complete history of the topic. You just need to help someone solve a problem they actually have.&lt;/p&gt;




&lt;h2&gt;
  
  
  Write for yourself first
&lt;/h2&gt;

&lt;p&gt;There's another aspect of writing that's often overlooked: writing for yourself.&lt;/p&gt;

&lt;p&gt;As Addy Osmani writes in his excellent article &lt;a href="https://addyosmani.com/blog/write-learn/" rel="noopener noreferrer"&gt;Write Learn&lt;/a&gt;, writing is one of the best ways to learn. When you try to explain something to others, you force yourself to understand it more deeply. You spot gaps in your own knowledge. &lt;a href="https://lukapeharda.com/article/write-before-you-think/" rel="noopener noreferrer"&gt;You clarify your own thinking&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So don't just write for others. Write for yourself. Write to learn. Write to clarify your own thoughts. The fact that others might benefit from it is just a happy side effect.&lt;/p&gt;

&lt;p&gt;I've learned more from writing articles than I have from reading them. There's something about the act of putting your thoughts into words that forces clarity in a way that nothing else does.&lt;/p&gt;




&lt;h2&gt;
  
  
  The courage to explain who you are
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;"We write for the same reason that we walk, talk, climb mountains or swim the oceans - because we can. We have some impulse within us that makes us want to explain ourselves to other human beings. That's why we paint, that's why we dare to love someone - because we have the impulse to explain who we are. Not just how tall we are, or thin... but who we are internally... perhaps even spiritually. There's something, which impels us to show our inner-souls. The more courageous we are, the more we succeed in explaining what we know." -- Maya Angelou&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's the thing about writing. It's not just about the information. It's about the person behind the information. It's about your unique perspective, your experiences, your voice.&lt;/p&gt;

&lt;p&gt;And that's what makes it valuable. Not the perfection of the prose. Not the length of the article. Not the number of fancy words you use. But the authenticity of the person writing it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Practical tips that actually work
&lt;/h2&gt;

&lt;p&gt;Okay, enough philosophy. Let me give you some practical advice you can actually use.&lt;/p&gt;

&lt;h3&gt;
  
  
  Before you write
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Pick one idea.&lt;/strong&gt; As I wrote in my article &lt;a href="https://lukapeharda.com/article/one-idea-one-article/" rel="noopener noreferrer"&gt;One idea, one article&lt;/a&gt;, trying to cram too many ideas into one article is like stuffing 10 clowns into a tiny car - chaos!&lt;/p&gt;

&lt;p&gt;One clear idea. One focused article. That's the way to go.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Know your audience.&lt;/strong&gt; Who are you writing for? What do they already know? What do they need to know? What problems are they facing?&lt;/p&gt;

&lt;p&gt;The more specific you can be about your audience, the better your writing will be.&lt;/p&gt;

&lt;h3&gt;
  
  
  While you're writing
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Write like you talk.&lt;/strong&gt; Your writing should sound like you. Not like a corporate press release. Not like a academic paper. Like you, talking to a friend.&lt;/p&gt;

&lt;p&gt;Use simple words. Use short sentences. Don't try to sound smart. Try to sound clear.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use examples.&lt;/strong&gt; Nothing makes a concept clearer than a good example. Especially in technical writing. Show code. Show &lt;strong&gt;your&lt;/strong&gt; real-world scenarios. Show what happens when things go wrong.&lt;/p&gt;

&lt;p&gt;People remember stories and examples long after they've forgotten the theory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Be honest.&lt;/strong&gt; Don't pretend to know things you don't. Don't be afraid to say "I don't know" or "I'm not sure about this part."&lt;/p&gt;

&lt;p&gt;Readers appreciate honesty. They can spot BS from a mile away. And they respect authenticity.&lt;/p&gt;

&lt;h3&gt;
  
  
  After you write
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Edit ruthlessly.&lt;/strong&gt; Your first draft is for you. Your second draft is for your readers.&lt;/p&gt;

&lt;p&gt;Cut anything that doesn't add value. Remove filler words. Shorten long sentences. Break up long paragraphs.&lt;/p&gt;

&lt;p&gt;Ask yourself: does this sentence add value? If not, cut it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Read it out loud.&lt;/strong&gt; This is one of the best editing tricks I know. If it sounds awkward when you say it, it'll read awkward when someone else reads it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get feedback.&lt;/strong&gt; Show your writing to someone else. Ask them: does this make sense? Is it valuable? What's confusing? What's missing?&lt;/p&gt;

&lt;p&gt;You don't need a professional editor. You just need someone who's willing to give you honest feedback.&lt;/p&gt;




&lt;h2&gt;
  
  
  What about SEO and all that?
&lt;/h2&gt;

&lt;p&gt;I'm not writing for Google. I'm not writing for robots. I'm not writing for SEO.&lt;/p&gt;

&lt;p&gt;I'm writing for you, and me.&lt;/p&gt;

&lt;p&gt;Does that mean I ignore SEO completely? No. I still use descriptive titles. I still use clear headings. I still try to make my articles discoverable.&lt;/p&gt;

&lt;p&gt;But I don't let SEO dictate what I write or how I write it. I don't stuff my articles with keywords just to rank higher. I don't write 3,000-word epics just because Google supposedly prefers long content.&lt;/p&gt;

&lt;p&gt;I write articles that provide value. And if Google rewards that, great. If not, I still have the satisfaction of knowing I helped someone.&lt;/p&gt;




&lt;h2&gt;
  
  
  The most important rule
&lt;/h2&gt;

&lt;p&gt;Here's the most important rule of writing: &lt;strong&gt;write.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not "write perfectly." Not "write when you have time." Not "write when you feel inspired."&lt;/p&gt;

&lt;p&gt;Just write.&lt;/p&gt;

&lt;p&gt;The best way to become a better writer is to write. A lot. Consistently. Even when it's hard. Even when you don't feel like it. Especially when you don't feel like it.&lt;/p&gt;

&lt;p&gt;You don't need to publish everything you write. You don't need to show it to anyone. But you do need to write.&lt;/p&gt;

&lt;p&gt;Because here's the thing, you can read all the articles about writing. You can watch all the videos. You can take all the courses. But none of that will make you a better writer. Only writing will.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why write at all?
&lt;/h2&gt;

&lt;p&gt;Good question. Why should developers write?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://evilmartians.com/chronicles/why-should-developers-write-3-reasons-and-3-common-blocks" rel="noopener noreferrer"&gt;This article&lt;/a&gt; from Evil Martians covers it well, but here are my reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It helps you learn.&lt;/strong&gt; As I mentioned earlier, writing forces you to clarify your thoughts and identify gaps in your understanding.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It helps your career.&lt;/strong&gt; Developers who can communicate well are more valuable. They get promoted faster. They get better jobs. They have more influence.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It helps others.&lt;/strong&gt; Every article you write has the potential to help someone else. To save them time. To save them frustration. To help them avoid a mistake you made.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It helps the community.&lt;/strong&gt; The developer community thrives on shared knowledge. Every article, every tutorial, every Stack Overflow answer makes us all better.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>writing</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <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://dev.to/lukapeharda/do-we-still-need-nosql-in-2026-4p2m</link>
      <guid>https://dev.to/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://dev.to/lukapeharda/why-i-hate-black-fridays-a-developers-thurs-dread-manifesto-48ep</link>
      <guid>https://dev.to/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://dev.to/lukapeharda/when-does-the-tdd-approach-make-sense-439j</link>
      <guid>https://dev.to/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://dev.to/lukapeharda/code-refactoring-what-is-it-and-why-should-you-do-it-1g3d</link>
      <guid>https://dev.to/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://dev.to/lukapeharda/use-exists-instead-of-count-0-when-checking-if-records-exist-a0k</link>
      <guid>https://dev.to/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://dev.to/lukapeharda/using-tailwind-css-classes-in-markdown-357i</link>
      <guid>https://dev.to/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://dev.to/lukapeharda/100-lines-of-code-per-file-2me9</link>
      <guid>https://dev.to/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://dev.to/lukapeharda/100-lines-of-code-per-file-47bi</link>
      <guid>https://dev.to/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://dev.to/lukapeharda/unexpected-benefits-of-estimations-5085</link>
      <guid>https://dev.to/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://dev.to/lukapeharda/how-to-generate-a-full-tailwind-css-palette-from-a-single-color-1mb1</link>
      <guid>https://dev.to/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://dev.to/lukapeharda/why-wrapping-3rd-party-calls-to-external-services-is-always-a-good-idea-46ae</link>
      <guid>https://dev.to/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>
  </channel>
</rss>
