<?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: Ilya Kaznacheev</title>
    <description>The latest articles on DEV Community by Ilya Kaznacheev (@ilyakaznacheev).</description>
    <link>https://dev.to/ilyakaznacheev</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%2F185959%2Fe9bfd345-11b8-4d6c-8e4f-504f2088d459.png</url>
      <title>DEV Community: Ilya Kaznacheev</title>
      <link>https://dev.to/ilyakaznacheev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ilyakaznacheev"/>
    <language>en</language>
    <item>
      <title>Step outside the Happy Path</title>
      <dc:creator>Ilya Kaznacheev</dc:creator>
      <pubDate>Wed, 04 Dec 2024 08:17:50 +0000</pubDate>
      <link>https://dev.to/ilyakaznacheev/step-outside-the-happy-path-47</link>
      <guid>https://dev.to/ilyakaznacheev/step-outside-the-happy-path-47</guid>
      <description>&lt;p&gt;In software engineering, the term &lt;em&gt;happy path&lt;/em&gt; refers to the ideal scenario where everything works as planned. It's natural to begin system design by focusing on these positive scenarios - they’re simple, straightforward, and align with how most people envision their product functioning.&lt;/p&gt;

&lt;p&gt;However, stopping at the happy path is a mistake. While it’s often sufficient for stakeholders from business backgrounds who lack technical expertise, it fails to address the majority of a system’s complexity. Robust systems excel not in perfect conditions but in their ability to handle problems and recover from failures.&lt;/p&gt;

&lt;p&gt;An architect, technical lead, or principal engineer plays a crucial role in ensuring that all scenarios - not just the happy path - are considered and explained to non-technical stakeholders. Their leadership helps foster a team culture that encourages proactive thinking and the design of resilient systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Seeing the Bigger Picture
&lt;/h2&gt;

&lt;p&gt;Experienced engineers - whether architects or senior developers - stand out by having a bird’s-eye view of the systems they build. This means recognizing areas without clear ownership, identifying potential issues, and ensuring product integrity across all teams and components.&lt;/p&gt;

&lt;p&gt;In complex systems, particularly distributed ones, overall availability is a product of the availability of each component. Components like microservices, databases, and messaging systems interact with infrastructure elements such as virtual machines, networks, and storage. Failure in one part, such as data consistency or error handling, can lead to cascading issues. Senior engineers are responsible for identifying these risks early and implementing solutions that ensure the system works as a cohesive whole.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg15r3fk313e5ws62wn1v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg15r3fk313e5ws62wn1v.png" alt="Thinking about a problem" width="800" height="547"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Mapping Out Scenarios
&lt;/h3&gt;

&lt;p&gt;A complete understanding of the system is essential to account for every potential scenario. This is especially critical in brownfield projects, where undocumented processes or unaddressed questions are common.&lt;/p&gt;

&lt;p&gt;Start by formalizing the architecture. You don’t need heavy enterprise methodologies like TOGAF; use formats that fit the team and product. Frameworks like &lt;a href="https://arc42.org/overview" rel="noopener noreferrer"&gt;arc42&lt;/a&gt; are suitable for complex systems, while simpler projects may only need &lt;a href="https://c4model.com" rel="noopener noreferrer"&gt;C4 diagrams&lt;/a&gt; supplemented with a few additional visualizations.&lt;/p&gt;

&lt;p&gt;Identify all data flows within the system. This will help you uncover gaps and overlooked details. At each step, consider what could go wrong. Create a list of possible events, ensuring it is &lt;a href="https://en.wikipedia.org/wiki/MECE_principle" rel="noopener noreferrer"&gt;MECE&lt;/a&gt;. This practice minimizes blind spots and ensures that every edge case is addressed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prioritizing Issues
&lt;/h3&gt;

&lt;p&gt;Not all potential problems are equally likely or impactful. For instance, network errors occur more frequently than hard drive failures, and both are more common than data corruption caused by solar flares.&lt;/p&gt;

&lt;p&gt;Similarly, not all problems are equally critical. Losing a log entry is often acceptable, but errors in customer billing are not. Combining the likelihood and importance of each issue helps prioritize which scenarios to address immediately and which can wait. A good system balances proactive preparation with realistic limitations, as it’s impossible to account for every scenario.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dealing with the Unknown
&lt;/h3&gt;

&lt;p&gt;Even the most thorough analysis won’t uncover every issue. Some problems only become apparent after they occur. For example, throttling in Kubernetes can disrupt Kafka’s heartbeat mechanism, triggering endless rebalancing. Or a misconfigured high-availability setup for virtual machines might cause PostgreSQL transactions to freeze, leading to application crashes. As you can guess, I only discovered these problems when they occurred.&lt;/p&gt;

&lt;p&gt;These "unknown unknowns" require systems to be designed for self-healing. For example, managed cloud Kubernetes platforms (e.g., GKE, AKS, EKS) monitor cluster nodes and automatically replace them if they become unresponsive. This ensures the system recovers, regardless of whether the issue was caused by software bugs, network failures, or even server rack fires.&lt;/p&gt;

&lt;p&gt;A system capable of self-healing must have a clear understanding of its current and desired states, along with mechanisms to return to the desired state automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Communicating with Stakeholders
&lt;/h2&gt;

&lt;p&gt;Building resilient systems is essential but often expensive. Architects and engineers must communicate with decision-makers - especially those controlling budgets - about the value of investing in solutions for potential problems.&lt;/p&gt;

&lt;p&gt;CFOs and other executives may argue, "Our customers only pay for the happy path". While true to an extent, the happy path alone cannot guarantee long-term reliability or customer satisfaction. Effective communication with non-technical stakeholders requires speaking their language: the language of business.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fln4toxr3ctcmfit7dtq4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fln4toxr3ctcmfit7dtq4.png" alt="Presenting a solution" width="800" height="547"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Explaining the Business Impact
&lt;/h3&gt;

&lt;p&gt;Every technical decision has financial implications. Calculate and present these clearly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How much will it cost if the system fails to handle non-happy-path scenarios?&lt;/li&gt;
&lt;li&gt;How likely are these scenarios?&lt;/li&gt;
&lt;li&gt;What are the potential losses in revenue, reputation, or customer trust?&lt;/li&gt;
&lt;li&gt;How much will the solution cost to develop and maintain?&lt;/li&gt;
&lt;li&gt;What additional benefits could the solution provide?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, a happy path might assume that a request in a distributed system will complete within 30 seconds. However, as the number of services grows or their workload increases, some requests may exceed the timeout, triggering cascading failures. These might require rollback of distributed transactions, and retrying requests could overload the system, potentially causing hours-long downtime.&lt;/p&gt;

&lt;p&gt;One solution could be to shift to asynchronous communication, at least for certain processes. This approach requires implementing and maintaining a message broker, adapting applications, and training engineers to work with a new communication paradigm. While time-consuming and resource-intensive, it would eliminate timeout issues, allow virtually unlimited scaling for critical processes, and enhance fault tolerance through delivery guarantees.&lt;/p&gt;

&lt;p&gt;Ultimately, the decision is not solely technical. If a company is under financial pressure, immediate profit from user-facing features might take priority. On the other hand, if the company’s strategy is focused on growth, scalability might align well with its goals.&lt;/p&gt;

&lt;p&gt;By speaking the language of money, you can make technical proposals resonate with business decision-makers. Conversely, if you can’t justify a solution financially, it might not be beneficial for the company.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bring Solutions, Not Just Problems
&lt;/h3&gt;

&lt;p&gt;People tend to dislike hearing about problems without solutions, even if they claim otherwise. When discussing issues with non-technical stakeholders, avoid leaving problems "hanging". Busy individuals already have enough challenges to address without unresolved technical issues added to the mix. Always present a few potential solutions to provide a starting point.&lt;/p&gt;

&lt;p&gt;Problem-solving is a highly valued skill in engineering. Often, exploring solutions independently eliminates the need to escalate issues, allowing you to bring fully-formed answers to your manager or client. This initiative demonstrates reliability and builds trust, as long as the solutions are well thought out.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Sooner, the Better
&lt;/h3&gt;

&lt;p&gt;Architectural decisions are costly and difficult to change later. The same applies to "problematic" scenarios - delays in addressing them can make implementation significantly more expensive.&lt;/p&gt;

&lt;p&gt;That’s why it’s critical to discuss and resolve such scenarios as early as possible. Of course, this can only happen once you become aware of them. How can you identify these scenarios sooner?&lt;/p&gt;

&lt;h2&gt;
  
  
  Building an Engineering Culture
&lt;/h2&gt;

&lt;p&gt;In traditional &lt;strong&gt;waterfall&lt;/strong&gt; methodologies, architects spend significant time analyzing potential risks and solutions before writing any code. While thorough, this approach is often inefficient for projects in dynamic contexts.&lt;/p&gt;

&lt;p&gt;With &lt;strong&gt;agile&lt;/strong&gt; methodologies, teams begin building solutions quickly, addressing problems as they arise. While this reduces time spent on unnecessary scenarios, it risks overlooking critical details and under-planning architecture.&lt;/p&gt;

&lt;p&gt;Regardless of methodology, early identification of problems depends not only on analytical tools but also on fostering a strong team culture.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F06a3xep79f4unc9elako.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F06a3xep79f4unc9elako.png" alt="Fostering engineering culture" width="800" height="547"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Open Discussions About Problems
&lt;/h3&gt;

&lt;p&gt;Unlike business communication, internal team discussions should be open and direct about potential issues. Encourage brainstorming and collaborative problem-solving to explore various options.&lt;/p&gt;

&lt;p&gt;Team members must feel safe raising concerns without fear of blame. Unfortunately, in some workplaces, the "no good deed goes unpunished" culture prevails, where the person who identifies an issue is burdened with solving it alone. This discourages initiative and erodes both team morale and system quality over time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building a Problem-Solving Culture
&lt;/h3&gt;

&lt;p&gt;While early identification of problematic scenarios is crucial, it’s equally important to cultivate a team culture that values open communication and initiative. Encourage team members to raise concerns without fear of blame. Collaborative brainstorming can often lead to innovative solutions.&lt;/p&gt;

&lt;p&gt;Avoid siloed thinking where teams focus solely on their responsibilities. A sense of ownership over the broader product helps engineers identify risks in gray areas that might otherwise be overlooked. This approach prevents costly issues and fosters a more adaptable and resilient organization.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;While identifying and addressing the full range of potential events is a key responsibility of architects and senior engineers, it’s a team effort. Early detection and proactive solutions benefit the product and the company. Communicating these issues effectively, especially to non-technical stakeholders, requires a focus on financial impact and actionable solutions.&lt;/p&gt;

&lt;p&gt;No system can predict every failure, but by addressing the most critical and likely scenarios, you ensure resilience. For everything else, self-healing mechanisms provide a safety net, allowing the system to recover independently. By fostering a culture of open communication and initiative, you enable teams to build systems that thrive in an unpredictable world.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>cloud</category>
      <category>leadership</category>
      <category>microservices</category>
    </item>
    <item>
      <title>Product Architecture Cheat Sheet</title>
      <dc:creator>Ilya Kaznacheev</dc:creator>
      <pubDate>Tue, 05 Dec 2023 23:00:00 +0000</pubDate>
      <link>https://dev.to/ilyakaznacheev/product-architecture-cheat-shee-4lj3</link>
      <guid>https://dev.to/ilyakaznacheev/product-architecture-cheat-shee-4lj3</guid>
      <description>&lt;p&gt;In this article, I will give you a no-brainer for choosing the right architecture for any project. Right doesn't mean perfect, but it does mean good enough.&lt;/p&gt;

&lt;p&gt;You may or may not be a CTO, Lead Architect, or Principal SWE. You may be working on a greenfield or brownfield project. You may work in a startup or a megacorp. But at some point, you will be at the very beginning of a tech product, and you will need to choose a proper architecture for it. At least for MVP. At least before the first paying customers. At least before the first million users. At least...&lt;/p&gt;

&lt;p&gt;There will be no deep analysis or theory. Just pure practice based on my own experience and knowledge. If your startup goes to the moon after reading this article, you can buy me a coffee.&lt;/p&gt;

&lt;h2&gt;
  
  
  Size Matters
&lt;/h2&gt;

&lt;h3&gt;
  
  
  One Man Army
&lt;/h3&gt;

&lt;p&gt;Are you building an MVP, PoC, or read-only code to be thrown away after the first round of investment? Say no more - your soul architecture is &lt;strong&gt;Monolith&lt;/strong&gt;. Yeah, it's not swag enough, nothing shiny to share at the smoothie unicorn meetup. There are bad rumors about Monolith, but fear not. It has proven to be very robust and powerful. Just think of &lt;a href="https://en.wikipedia.org/wiki/Separation_of_concerns"&gt;SoC&lt;/a&gt;, invest in some readable code, use proper tools, and do not over-engineer - and you will be fine. Monolith will save you a lot of time, so much precious at this stage of your product - better spend this time improving your product or finally sleep.&lt;/p&gt;

&lt;h3&gt;
  
  
  Small Team
&lt;/h3&gt;

&lt;p&gt;No longer MVP, but still small? Well, here we go again. &lt;strong&gt;Monolith&lt;/strong&gt; is your best friend. Don't spend a lot of time communicating between parties, fine-tuning protocols, authentication, zero trust, routing, and service discovery - just put everything you want into a single application! It shouldn't stink, though. Build boundaries, separate logical blocks, and introduce abstractions. Build as if there are microservices, just don't break your application into cans of separate processes. Interfaces instead of RPCs, database transactions instead of SAGAs, imports instead of DNS. Run fast, but don't skimp on quality - there's a recipe for successful teamwork.&lt;/p&gt;

&lt;h3&gt;
  
  
  Part Of The Ship, Part Of The Crew
&lt;/h3&gt;

&lt;p&gt;Do you have an ambitious team, do you work on an ambitious project, and it is expected to be huge? Does it belong to a single business domain? If so, &lt;strong&gt;Monolith&lt;/strong&gt; probably fits here too. And why is that? Because it is very likely that you will share many of the same business entities and logic across applications. Breaking it up into many separate deployments will just make everything more complex and slower without providing any real benefit. That doesn't mean your service can't scale and handle peak loads. Design the application to be stateless, allow for concurrency, and stay cloud-native - and you will be able to scale it with ease. Read my blog for more tips on scalability, or ask me for a scalability workshop for your company (my own ad in my own blog, why not?).&lt;/p&gt;

&lt;h3&gt;
  
  
  Rich Man In Poor Man's Shoes
&lt;/h3&gt;

&lt;p&gt;Have multiple domains, but still a small team? Not enough engineers to build a proper (5-7 members) team for each domain? I have news for you - &lt;strong&gt;Monolith&lt;/strong&gt; is your friend. Why? Because without dividing engineers into separate teams, microservice architecture &lt;a href="https://en.wikipedia.org/wiki/Conway%27s_law"&gt;will not work&lt;/a&gt;. It only makes sense if each team is independent in terms of organization, only then they will be able to build properly isolated services without a &lt;a href="https://www.infoq.com/news/2016/02/services-distributed-monolith/"&gt;distributed monolith&lt;/a&gt;. So why not build a centralized one? You still have all the tools to build a beautiful, well-architected, scalable, resilient service. But without a lot of the limitations that microservices impose.&lt;/p&gt;

&lt;p&gt;Why not Lambdas? Well, the answer is &lt;strong&gt;maybe Lambdas&lt;/strong&gt;. But you have to be sure that you know what you are doing. And so does your team. I will make a note about lambdas later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Higher, Stronger
&lt;/h2&gt;

&lt;h3&gt;
  
  
  All The Colors Of The Rainbow
&lt;/h3&gt;

&lt;p&gt;So you have many business domains, and your team is large enough to form independent domain groups? Congratulations, &lt;strong&gt;Microservices&lt;/strong&gt; will work for you. Just don't forget the ground rules - no shared databases, no shared models, no state - only stateless, only APIs, only cloud-native. Microservices are very expensive, not just in cloud costs. They are much harder to build, harder to deploy and run, and harder to monitor and troubleshoot. With microservices, you have to bring a whole layer of supporting tools and roles - proper infrastructure (like Kubernetes), CI/CD, distributed observability, DevOps, DevSecOps, API gateways, and so on. I mean, a mature product will have all that anyway. It's just too expensive for a startup and completely slows down a small team.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fear Is The Mind-Killer
&lt;/h3&gt;

&lt;p&gt;Want to bring something new to the table? Something like lambdas? But you're afraid that when it's time to go, you'll be stuck with a vendor solution or sacrifice for a so-so vendor-agnostic solution. This fear is real, unfortunately, I could have told you to go for some open source vendor agnostic tools like Knative, but I didn't. Because I don't believe in them myself. While we are still waiting for a Kubernetes-level production-ready WASM orchestrator, &lt;strong&gt;Microservices&lt;/strong&gt; architecture is still better for this case. Hopefully, this will change soon and the whole industry will move towards WASM-based architectures and tools.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Spirit Is Willing, But The Flesh Is Weak
&lt;/h3&gt;

&lt;p&gt;Still want lambdas, event-driven, reactive programming? Do you want functions, actors, monads? Well, brave one, ask yourself: does your team know all this shiny functional stuff? If the honest answer is no - bye-bye lambdas, hello &lt;strong&gt;Microservices&lt;/strong&gt;. Seriously, choose either business goals or cool concepts, unless your team members are Clojure gurus. Otherwise...&lt;/p&gt;

&lt;h2&gt;
  
  
  Cherry On Cake
&lt;/h2&gt;

&lt;p&gt;Yes, you deserve it. AWS Lambdas, Azure Functions, GCP Cloud Functions, Alibaba Functions Compute - you name it. You definitely know what you're doing, and your team worships the same god. Let go of the fear, and step into destiny. Let reactive architecture consume you. There is no turning back, and you know it. Enjoy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summing Up
&lt;/h2&gt;

&lt;p&gt;For your convenience, I've organized all the ideas into a chart that you can carry with you at all times.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--80sjxA_w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hdk8x4gxsiwwnkuwl7q2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--80sjxA_w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hdk8x4gxsiwwnkuwl7q2.png" alt="Image description" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>microservices</category>
      <category>monolith</category>
      <category>lambda</category>
    </item>
    <item>
      <title>Virtual Machine Scaling</title>
      <dc:creator>Ilya Kaznacheev</dc:creator>
      <pubDate>Tue, 26 Jul 2022 10:44:00 +0000</pubDate>
      <link>https://dev.to/ilyakaznacheev/virtual-machine-scaling-4mib</link>
      <guid>https://dev.to/ilyakaznacheev/virtual-machine-scaling-4mib</guid>
      <description>&lt;p&gt;Virtual machines (VMs) are the basic building block of cloud infrastructure - all cloud products like databases, orchestrators, message queues, functions and others are based on them. Many companies also use virtual machines as the foundation of their cloud landscape, running all sorts of services and applications on them.&lt;/p&gt;

&lt;p&gt;In this article, I will explain ways and approaches for scaling VMs.&lt;/p&gt;

&lt;p&gt;Virtual machines give the user more power and control over the environment and applications, but require greater skill and more atomic configuration. Therefore, scaling a system based on virtual machines is less flexible than the alternatives I will be discussing in other articles. So how do you configure virtual machine scaling to increase resource utilization on the one hand, but on the other hand be prepared for peaks in load?&lt;/p&gt;

&lt;p&gt;There are several ways to solve this problem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;manual scaling;&lt;/li&gt;
&lt;li&gt;automatic scaling.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's go into more detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Manual Scaling
&lt;/h2&gt;

&lt;p&gt;When operating virtual machines, a situation may arise when the resources allocated to them (CPU, RAM) become insufficient. To solve the problem, manual scaling is performed - a set of actions that the administrator takes to change the amount of system resources, usually upwards.&lt;/p&gt;

&lt;p&gt;But what exactly do you need to do? There are two main ways of scaling in this case:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;vertical scaling - changing the resources of a single machine;&lt;/li&gt;
&lt;li&gt;horizontal scaling - changing the number of machines without changing their configuration.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Vertical Scaling
&lt;/h3&gt;

&lt;p&gt;When scaling vertically, you can change the main resources of the machine - CPU and RAM. This usually requires stopping and then restarting the virtual machine. You can also change the size of the virtual disk mounted to the machine, but in this case, you will need to make changes to the file system as well.&lt;/p&gt;

&lt;p&gt;Vertical scaling is widely used for partition intolerant (&lt;a href="https://en.wikipedia.org/wiki/CAP_theorem" rel="noopener noreferrer"&gt;CA&lt;/a&gt;) systems, which can only run in a single instance. Examples of such systems are classical databases like PostgreSQL and MySQL, as well as applications based on &lt;a href="https://microservices.io/patterns/monolithic.html" rel="noopener noreferrer"&gt;a monolithic architecture&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwukngvc9dcnx807h8l3d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwukngvc9dcnx807h8l3d.png" alt="vertical scaling"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the easiest solution when scaling, since it does not require any changes to the applications. It is the first thing to go to when your system lacks resources.&lt;/p&gt;

&lt;p&gt;However, server costs increase non-linearly as resources increase, so at a certain point, this type of scaling becomes too expensive. If you need to further scale your system, horizontal scaling is the way to go.&lt;/p&gt;

&lt;h3&gt;
  
  
  Horizontal Scaling
&lt;/h3&gt;

&lt;p&gt;This type of scaling assumes that as the load changes, the number of servers handling that load changes proportionally, while the configuration of the machines remains the same. To do this, you can either simply create additional machines manually, or create them as part of an unmanaged instance group.&lt;/p&gt;

&lt;p&gt;In the first case, the administrator is responsible for creating and configuring the new virtual machine and putting it into operation. Using instance group, on the other hand, makes the process somewhat easier, as the machine is created from a template with a fixed configuration. However, regardless of the way new machines are created, the development of a horizontally scalable system has a number of complexities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the application must support multiple replicas running in parallel;&lt;/li&gt;
&lt;li&gt;the infrastructure must be able to deliver data to all replicas;&lt;/li&gt;
&lt;li&gt;the infrastructure must strive to distribute the load evenly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Consideration of these complexities deserves a separate article, but today's systems are evolving in this direction.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9sey7pnn21ni72otwv4s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9sey7pnn21ni72otwv4s.png" alt="horizontal scaling"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, manual changes can lead to untimely responses to load changes. You may miss when the load has increased, and thus lose some requests or data, which can lead to serious business losses. On the other hand, a decrease in load can also go unnoticed, causing purchased servers to sit idle without load.&lt;/p&gt;

&lt;p&gt;To make the system more self-sufficient and able to respond quickly to load changes, it is worth taking advantage of automatic scaling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automatic Scaling
&lt;/h2&gt;

&lt;p&gt;Automatic scaling involves running virtual machines within a managed instance group. Such a group independently monitors the state of the machines in the group and the distribution of load on them. When load changes, the group decides whether to add or remove machines to the group.&lt;/p&gt;

&lt;p&gt;The criteria on the basis of which the group decides to scale are flexible. For example, it can be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPU utilization threshold in percentage;&lt;/li&gt;
&lt;li&gt;number of http requests per machine;&lt;/li&gt;
&lt;li&gt;number of queued messages per machine;&lt;/li&gt;
&lt;li&gt;customizable metrics based on monitoring data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Such options allow for flexible autoscaling depending on the processing scenarios of a particular system. In addition, many providers offer predictive scaling tools powered by analytics or machine learning. Such tools learn from real-world scenarios of machine usage and load changes, and optimize scaling in ways that increase resource utilization.&lt;/p&gt;

&lt;h3&gt;
  
  
  Managed Instance Group
&lt;/h3&gt;

&lt;p&gt;To control a fleet of machines, a managed instance group is created with a specified setting. Often these settings include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;location (region and zone or zones);&lt;/li&gt;
&lt;li&gt;scaling settings (type and parameters);&lt;/li&gt;
&lt;li&gt;network settings;&lt;/li&gt;
&lt;li&gt;virtual machine template.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The template must start the service at VM startup, so that it starts itself and gets configured if necessary (e.g., with service discovery). This is necessary so that when instances are automatically added to a group, they are started automatically. I usually use systemd for this but the cloud init will also work.&lt;/p&gt;

&lt;p&gt;As for the scaling settings, you select the type and parameters, as well as the minimum and maximum number of machines in the group.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq2hbd37l1igdgfr8xisk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq2hbd37l1igdgfr8xisk.png" alt="instance group autoscaling"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When the load is low, the number of instances will automatically decrease until it reaches a minimum number. This number will be kept until the load exceeds the threshold again.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff6cg5yv0lo9gna6i0oey.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff6cg5yv0lo9gna6i0oey.png" alt="instance group autoscaling minimum"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the other hand, when the load is high, the number of machines can reach a maximum, after which new machines will stop being added no matter how the system handles the load.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbz6ihc8lffboyu3fbq4r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbz6ihc8lffboyu3fbq4r.png" alt="instance group autoscaling maximum"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When creating an instance group, it is important to verify that your quotas and limits allow you to create the required number of machines and allocate the required number of network addresses to them.&lt;/p&gt;

&lt;p&gt;Note that each machine in the group will have a different IP address. For the network communication with the instance group, load balancers are used, which I will talk about in the next article.&lt;/p&gt;

&lt;h2&gt;
  
  
  How To Set Up An Instance Group In Google Cloud
&lt;/h2&gt;

&lt;p&gt;To set up a managed instance group in GCP, you must first create a virtual machine template with the application. To do this, just create a regular virtual machine in the Google Compute Engine based on the operating system of your choice. In my example I created a virtual machine based on Debian and installed the &lt;a href="https://github.com/ilyakaznacheev/cpuburn-web" rel="noopener noreferrer"&gt;cpuburn&lt;/a&gt; application that I will use to emulate CPU load.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvfmxmfa6vo1a5i29riw9.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvfmxmfa6vo1a5i29riw9.jpeg" alt="create instance"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In order for the application to start automatically at the virtual machine startup, the service must be configured, in my example at &lt;code&gt;/etc/systemd/system/cloud-scaling.service&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Cloud Scaling Example&lt;/span&gt;

&lt;span class="nn"&gt;[Service]&lt;/span&gt;
&lt;span class="py"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;+${PWD}/cpuburn-web -p 80&lt;/span&gt;

&lt;span class="nn"&gt;[Install]&lt;/span&gt;
&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And started.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl daemon-reload
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; cloud-scaling
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The virtual machine must then be stopped in order to create an image based on its disk.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdg4o5p01zz9o1pxhzzaz.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdg4o5p01zz9o1pxhzzaz.jpeg" alt="create image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next go to Instance templates and create a new one. You also need to enable HTTP traffic in firewall settings.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Few018e64t456n05tnxtp.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Few018e64t456n05tnxtp.jpeg" alt="create template"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Change boot disk to the image you've just created.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fniubmzskddqopyf0jzy2.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fniubmzskddqopyf0jzy2.jpeg" alt="choose image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we have everything we need to create an instance group based on this template.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgzzhrsptk34c0hlnxl5w.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgzzhrsptk34c0hlnxl5w.jpeg" alt="create instance group"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Specify the minimum and maximum number of machines in the group, add one or more autoscaling criteria.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjk47uphwc3su432ok2qx.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjk47uphwc3su432ok2qx.jpeg" alt="setup instance group"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After the group is created, one virtual machine will be started, matching the minimum number.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fepc72hkud3t5jk7fqrgf.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fepc72hkud3t5jk7fqrgf.jpeg" alt="check instance group"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's burn some CPU! I'll trigger cpuburn application on the node and see what happens next.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo2nu55kq4tz0se9n13xo.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo2nu55kq4tz0se9n13xo.jpeg" alt="check instance group"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The monitoring tab shows the dynamics of adding virtual machines, as well as changes in the total CPU utilization - when cpuburn started, it jumped up dramatically, but after adding new machines ( and therefore new resources) fell down again.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F281uynctzbjg0voi3jex.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F281uynctzbjg0voi3jex.jpeg" alt="instance group metrics"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After disabling cpuburn, the instance group removes unused machines after a while...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxmj3h47nvniin49eddop.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxmj3h47nvniin49eddop.jpeg" alt="deleting instances"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;...going back to the minimum number again.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Focq1zchqky4r90m2v6nv.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Focq1zchqky4r90m2v6nv.jpeg" alt="deleting instances"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Looking at the metrics again, we can see the history of changes in the virtual machine count in the group.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe3uo2a1j8jj6s2k8wlch.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe3uo2a1j8jj6s2k8wlch.jpeg" alt="history"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With these simple steps, you can configure a fault-tolerant system capable of withstanding large load spikes. The flexibility of the scaling criteria allows you to adjust for different usage scenarios and different types of input data.&lt;/p&gt;

&lt;p&gt;However, the low speed of creating and deleting instances makes this tool inconvenient for systems with sudden jumps in load, demanding to process the maximum number of requests. For such tasks, other cloud products are better suited, which I will describe in the following articles.&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>scaling</category>
      <category>googlecloud</category>
    </item>
    <item>
      <title>Where to Place Logger in Golang?</title>
      <dc:creator>Ilya Kaznacheev</dc:creator>
      <pubDate>Sat, 25 Jun 2022 09:52:00 +0000</pubDate>
      <link>https://dev.to/ilyakaznacheev/where-to-place-logger-in-golang-13o3</link>
      <guid>https://dev.to/ilyakaznacheev/where-to-place-logger-in-golang-13o3</guid>
      <description>&lt;p&gt;Logging is an integral part of any application. However, the correct location and use of the logger in the project structure raises questions even for experienced developers.&lt;/p&gt;

&lt;p&gt;There are several ways of doing this, of which I give preference to one. I will explain why.&lt;/p&gt;

&lt;p&gt;When designing an application, the developer chooses from several explicit options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;store the logger in a global variable;&lt;/li&gt;
&lt;li&gt;get the logger from the logging library;&lt;/li&gt;
&lt;li&gt;add the logger to data structures;&lt;/li&gt;
&lt;li&gt;pass the logger explicitly in the function call.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article, I will use &lt;a href="https://github.com/uber-go/zap"&gt;Zap logger&lt;/a&gt; as an example, but you may use any.&lt;/p&gt;

&lt;h2&gt;
  
  
  Store The Logger in a Global Variable
&lt;/h2&gt;

&lt;p&gt;This is the simplest option that comes to mind.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;zap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewProduction&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is good in its simplicity, but its implementation can be inefficient:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the use of global variables increases the coupling in the program, worsening the structure of the code;&lt;/li&gt;
&lt;li&gt;the global logger is inconvenient to adjust for a particular context, for example - to add common fields for a message set to the logger;&lt;/li&gt;
&lt;li&gt;defining a global scope can be tricky for an application with multiple executable files.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Get The Logger from the Logging Library
&lt;/h2&gt;

&lt;p&gt;This option is almost the same as the global variable, only the variable is hidden inside the imported module.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;zap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;L&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 a little more convenient to use, because you don't have to search for the global variable in the code and import it. On the other hand, it is possible to get a library version conflict when plugging in external dependencies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add The Logger to Data Structures
&lt;/h2&gt;

&lt;p&gt;This is a fairly common alternative used by many developers. However, its use adds an element to structures that does not relate to the data structure, but to their instrumentation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;UserRepository&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;zap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;
    &lt;span class="c"&gt;/*
    other fields
    */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ur&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;UserRepository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;SaveUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ur&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"saving user"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c"&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 other words, logging is not related to data, but to operations, so it makes more sense to have it in functions and methods rather than keeping it in structures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pass The Logger Explicitly in the Function Call
&lt;/h2&gt;

&lt;p&gt;It turns out that the most logical place for a logger is in the methods or functions themselves. However, passing a logger into every function obviously leads to a huge code complication:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adding a parameter to a function will worsen the readability of the signature;&lt;/li&gt;
&lt;li&gt;explicitly passing the logger along the call chain adds a lot of copy-paste;&lt;/li&gt;
&lt;li&gt;functions with a fixed signature (e.g., HTTP handler) will require you to pass the logger through workarounds.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;CreateUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;zap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;With&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"userID"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user creation started"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;SaveUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user creation failed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;zap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user creation done"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes the approach inapplicable in practice. But is there a way to combine the pros of the different approaches, avoiding the cons?&lt;/p&gt;

&lt;h2&gt;
  
  
  The Win-Win Context
&lt;/h2&gt;

&lt;p&gt;As a developer, I would like to pass the logger to a function without adding new parameters to its signature. But these requirements seem to be contradictory.&lt;/p&gt;

&lt;p&gt;Fortunately, there is an entity in Golang which is often passed to every function, and performs many utilitarian tasks - it is &lt;code&gt;context.Context&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Context in Go solves very important problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;gracefully interrupting processing at application shutdown;&lt;/li&gt;
&lt;li&gt;limiting the execution time of a function;&lt;/li&gt;
&lt;li&gt;managing asynchronous operations;&lt;/li&gt;
&lt;li&gt;getting out of infinite loops;&lt;/li&gt;
&lt;li&gt;distributed tracing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As we can see, these tasks are purely utilitarian, that is, they do not relate to the business logic of the application. The same is the task of logging (if we are not talking about special logging kinds, such as an audit log).&lt;/p&gt;

&lt;p&gt;This way, passing the log via context will not disrupt the function's signature (since most functions already take the context as a first parameter, and others will benefit from it). This can be done as follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"context"&lt;/span&gt;
    &lt;span class="s"&gt;"go.uber.org/zap"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;ctxLogger&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="c"&gt;// ContextWithLogger adds logger to context&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;ContextWithLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;zap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctxLogger&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// LoggerFromContext returns logger from context&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;LoggerFromContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;zap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctxLogger&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="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;zap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;zap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;L&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 will greatly simplify the use of the logger and provide additional benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;add common fields to the log along the call chain, such as adding a userID from a request;&lt;/li&gt;
&lt;li&gt;implement middleware for http/grpc, which will add to the log some information about the request;&lt;/li&gt;
&lt;li&gt;integrate log with other tools that use context, such as distributed tracing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In addition, the approach solves the issue of access to global data - now the logger is always local to the called function, and can be used without restrictions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;CreateUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LoggerFromContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;With&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"userID"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user creation started"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ContextWithLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;SaveUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user creation failed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;zap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user creation done"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In practice, it is not always convenient to use this approach, but it covers about 90%-95% of cases in a server application. For the rest of the cases, you can use another approach from those listed above.&lt;/p&gt;

&lt;p&gt;It also comes in handy in teamwork, where different team members can add call context to the logger at different levels of the application as they work on their changes. This will eventually be combined into informative and useful records that are easy to work with.&lt;/p&gt;

</description>
      <category>go</category>
      <category>log</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Basics of cloud scaling</title>
      <dc:creator>Ilya Kaznacheev</dc:creator>
      <pubDate>Sun, 29 May 2022 00:00:00 +0000</pubDate>
      <link>https://dev.to/ilyakaznacheev/basics-of-cloud-scaling-17kh</link>
      <guid>https://dev.to/ilyakaznacheev/basics-of-cloud-scaling-17kh</guid>
      <description>&lt;p&gt;In this article, you will learn what application scaling is, and how cloud infrastructure simplifies the process and makes it more useful for business.&lt;/p&gt;

&lt;p&gt;Scalability &lt;a href="https://en.wikipedia.org/wiki/Scalability"&gt;according to Wikipedia&lt;/a&gt; is the property of a system to handle a growing amount of work by adding resources to the system. In other words, it is the ability of the system to increase or decrease the amount of used resources (RAM, CPU, network bandwidth, disk size, etc.) depending on the load - the number of requests per second, the amount of stored data, computational load, etc.&lt;/p&gt;

&lt;p&gt;When we talk about scaling, it's worth starting with defining the system itself. A system can be either monolithic or distributed, and depending on this, the way it scales is different.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scaling a Monolithic System
&lt;/h2&gt;

&lt;p&gt;This type of system has existed for quite some time, and is the easiest to implement. Usually the application comes as an executable file or a set of scripts run by the server. It runs on a physical server or a virtual machine, often together with a database and a reverse-proxy like Nginx. Thus, networking problems in the interaction of components are minimized, and the system itself can be delivered as a virtual machine template or even as an installation disk.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pdFGigNM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nwnin0lqb9lk61olk2l1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pdFGigNM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nwnin0lqb9lk61olk2l1.png" alt="vertical scaling" width="800" height="272"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The approach to scaling such systems is &lt;strong&gt;vertical scaling&lt;/strong&gt; - when the system runs out of resources, it restarts on a more powerful server with more resources.&lt;/p&gt;

&lt;p&gt;This used to work well when manufacturers were releasing computers twice as powerful every six months. In recent decades, however, the rate of releases has decreased as microchip capabilities have approached their physical limit. Therefore, more powerful computers today are much more expensive than their less powerful counterparts. And doubling the capacity can increase the cost by 5 or even 10 times.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dXWWrk8c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gm2v19fnzjhys7984qfw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dXWWrk8c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gm2v19fnzjhys7984qfw.png" alt="scaling cost" width="800" height="549"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So when you reach a certain point, it simply becomes too expensive to buy a more powerful server. It is much more cost-effective to buy a lot of cheap computers, getting the total of the same resources. But to operate a system based on a fleet of individual machines, it must be distributed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scaling a Distributed System
&lt;/h2&gt;

&lt;p&gt;The ability to scale the system is divided into three directions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;horisontal duplication;&lt;/li&gt;
&lt;li&gt;functional decomposition;&lt;/li&gt;
&lt;li&gt;data partitioning.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OKKxUhD7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jas3q75bi2audnrsfxyq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OKKxUhD7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jas3q75bi2audnrsfxyq.png" alt="distributed system scaling options" width="800" height="560"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's take a closer look.&lt;/p&gt;

&lt;h3&gt;
  
  
  Horisontal Duplication
&lt;/h3&gt;

&lt;p&gt;Horisontal duplication is about running multiple copies of the same application in parallel. The copies have the same functionality, and are called replicas. In most cases the load is evenly distributed between replicas. To distribute requests between replicas, a load balancer is used, but that will be discussed in another article.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--J8fVLH3W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/72oc29y7l1m9xsvmyrgp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--J8fVLH3W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/72oc29y7l1m9xsvmyrgp.png" alt="horisontal duplication" width="800" height="157"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The more replicas a service has, the more resources are available to it. When resources become insufficient to handle the load on the system, it is sufficient to run several new replicas, and the excess load will be processed.&lt;/p&gt;

&lt;p&gt;Horisontal duplication also helps to achieve increased service availability. In case one of the replicas becomes unavailable (application crash due to a bug, network problem, resource shortage and OOM Killer), the load will be distributed among the remaining replicas, and the load balancer will prevent data loss.&lt;/p&gt;

&lt;p&gt;This approach is also called as &lt;strong&gt;horisontal scaling&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Functional Decomposition
&lt;/h3&gt;

&lt;p&gt;In functional decomposition, system functionality is divided into separate components (e.g., microservices). Each domain of the system or part of the functionality is decoupled into a separate and independent service.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1cM3KgGp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1pjfcvyakec0su8z42wk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1cM3KgGp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1pjfcvyakec0su8z42wk.png" alt="functional decomposition" width="800" height="194"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This makes it possible to isolate the logic of individual loosely coupled parts of the system. This simplifies the process of service development and delivery, and reduces the probability of mutual dependencies and blocking between development teams.&lt;/p&gt;

&lt;p&gt;Teams become more independent - they can choose their own technologies and development tools, their own team collaboration frameworks (Scrum, Kanban, etc.), best suited to the nature of the application and the skills of team members. In doing so, the various services are no longer dependent on common parts of the application, libraries, or shared memory. They exchange data explicitly through APIs, so all that connects them to each other are synchronous and asynchronous message contracts.&lt;/p&gt;

&lt;p&gt;Most importantly, functional decomposition allows each individual service to scale according to its needs. In the case of a monolithic system, the scalability of an individual component is very limited. In the case of a distributed system, one of the components can be run on multiple replicas, while the other will be run on a more powerful machine&lt;/p&gt;

&lt;h3&gt;
  
  
  Data Partitioning
&lt;/h3&gt;

&lt;p&gt;Data partitioning is the splitting of stored data into several separate parts. For example, data stored in a single database is partitioned into several database instances, and finding a particular record in a particular database is determined by the &lt;em&gt;partitioning key&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HeklGDmR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w2fmlt75k4p5ed06kozc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HeklGDmR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w2fmlt75k4p5ed06kozc.png" alt="data partitioning" width="800" height="419"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This way you can get around the limitation of storing data on a disk - the maximum disk size is physically limited, and when the amount of data exceeds its size, partitioning allows you to divide the data among several disks and thus store more data.&lt;/p&gt;

&lt;p&gt;Another possibility of data partitioning is to distribute the query processing load among several databases. Typically, a database is a complex application that consumes a fair amount of CPU and RAM. As the number of queries or their complexity increases, the database consumes more and more resources. Data partitioning allows you to have more database instances, and therefore have more computing resources available to perform queries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cloud Benefit of Scaling
&lt;/h2&gt;

&lt;p&gt;Cloud platforms, unlike the bare metal or virtual machines available in conventional hosting, offer a simple infrastructure management option. For example, an administrator can configure the cloud infrastructure through a simple web interface, or through a CLI application, or via an API.&lt;/p&gt;

&lt;p&gt;It also provides a wide range of options for automating infrastructure management. From routine tasks automation through simple scripts using the CLI, to implementing the Infrastructure-as-a-Code approach using tools like Terraform.&lt;/p&gt;

&lt;p&gt;Finally, clouds also offer built-in infrastructure management solutions, such as automatic scaling (including smart self-learning autoscaling in the largest clouds). These tools allow you to spend less time configuring infrastructure so you can use it to grow your product.&lt;/p&gt;

&lt;p&gt;In the following articles, I will talk about specific cloud scaling options and teach you how to set up an autoscalable system by yourself.&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>scaling</category>
    </item>
    <item>
      <title>Clean Transactions in Golang Hexagon</title>
      <dc:creator>Ilya Kaznacheev</dc:creator>
      <pubDate>Mon, 14 Mar 2022 13:55:02 +0000</pubDate>
      <link>https://dev.to/ilyakaznacheev/clean-transactions-in-golang-hexagon-107d</link>
      <guid>https://dev.to/ilyakaznacheev/clean-transactions-in-golang-hexagon-107d</guid>
      <description>&lt;p&gt;How to implement transaction management in a hexagonal microservice? How to keep the isolation of the application layer and the database adapter? In this article I will share my experience in solving this problem.&lt;/p&gt;

&lt;p&gt;Clean architecture (aka onion architecture) is very popular in modern microservice development. This approach clearly answers a lot of architectural questions, and is also good for services with a small codebase. Another nice feature of clean architecture is that it combines well with Domain Driven Development - they complement each other perfectly. &lt;/p&gt;

&lt;p&gt;One of the applications of clean architecture is hexagonal architecture, an approach that explicitly distinguishes layers, adapters, and so on. This approach has gained love among Go developers because it does not require complex abstractions or intricate patterns, and does not contradict complicated language idiom - the so-called Go way.&lt;/p&gt;

&lt;p&gt;But there is a problem I often see in many teams adapting hexagons, which I myself have encountered and successfully solved - the implementation of database transactions within DDD and the very hexagon itself. I will tell you what I have done in this post.&lt;/p&gt;

&lt;h2&gt;
  
  
  The High Abstraction Problem
&lt;/h2&gt;

&lt;p&gt;A hexagonal architecture implies an inversion of dependencies as follows: at the center of everything is the data model, the domain logic is built around it (and depends on it), a layer of application logic is placed on top of it, and then there are adapters hidden behind interfaces called ports. This can vary, but the basic idea that dependencies diverge from the center to the periphery remains.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuly638chij656x1j623f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuly638chij656x1j623f.png" alt="hexagon"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As an example, let's imagine for a moment that we are doing a microservice that sells used cars.&lt;/p&gt;

&lt;p&gt;Let's imagine that one of the adapters is a module for database interaction. But not any random database, but one that supports ACID transactions. Interacting with the database is pretty easy to implement - we wrap the domain models in repositories, hide each repository behind an interface (port), and implement it inside the adapter. Such a port may look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="c"&gt;// CarRepository car persistence repository&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CarRepository&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;UpsertCar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;car&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
    &lt;span class="n"&gt;UpsertCarHistoryEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CarEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
    &lt;span class="n"&gt;GetCar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;GetCarsBatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ids&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;GetCarsByTimePeriod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;GetCarsByModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;DeleteCar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the domain logic side, this adapter will be passed as a DI through the interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;domain&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Car&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;carRepo&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CarRepository&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;NewCar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;carRepo&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CarRepository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Car&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;carRepo&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;carRepo&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;For example, the logic of the car search by year of manufacture would be as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;GetNewCars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;someValidation&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"invalid request: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// read latest cars&lt;/span&gt;
    &lt;span class="n"&gt;cars&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;carRepo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetCarsByTimePeriod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"newest cars read: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&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="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filterCars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cars&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works pretty well with simple atomic operations like create, delete, or read. But quite often you need to execute complex logic within a single database transaction. I won't describe the examples, you know them well enough.&lt;/p&gt;

&lt;p&gt;The issue here is that in terms of architecture a transaction is part of the database adapter - it is opened and closed with certain commands (&lt;code&gt;BEGIN&lt;/code&gt;, &lt;code&gt;COMMIT&lt;/code&gt; or &lt;code&gt;ROLLBACK&lt;/code&gt; in SQL), and has a binding to the generated entity - the transaction object. Transaction object itself is usually not hovering in the clouds of the global program scoop, but explicitly bound to the database connection session over the TCP connection. So we can't (and don't want to) abstractly declare "transaction start" and "transaction end" in business code. When opening a transaction we have some entity - a transaction object - which we need to pass to the adapter to perform database operations exactly within this transaction.&lt;/p&gt;

&lt;p&gt;This is where the chicken and egg problem arises. On the one hand, the adapter requires that for each query within a transaction, information about that transaction itself must be passed - usually the library used implements the transaction as some kind of object through which queries can be made. On the other hand, the domain or application layer should not know about the adapter implementation in the hexagon paradigm. You can wrap the transaction in some interface, but this interface will be monstrously huge (with methods like &lt;code&gt;Select&lt;/code&gt;, &lt;code&gt;Insert&lt;/code&gt;, &lt;code&gt;Delete&lt;/code&gt; and others - open your favorite SQL library and see how many methods there). And it will not make any sense to the domain - these methods will be used within the adapter, where there is a direct access to the transaction object without any abstraction.&lt;/p&gt;

&lt;p&gt;We could go another way and pass the transaction as &lt;code&gt;interface{}&lt;/code&gt; and then cast it to the desired type in the adapter via reflexion, but I consider this approach unserious and unsuitable for productive code. Besides, I don't want to pollute method signature by passing an additional transaction object, because it doesn't directly relate to the method itself. The transaction object a indicates the specifics of the whole process of working with the database as part of the operation.  So what to do? &lt;/p&gt;

&lt;h2&gt;
  
  
  Specific Implementations Solution
&lt;/h2&gt;

&lt;p&gt;Now a couple of words about the context of our solution. In my search for an elegant implementation, I have encountered several times solutions like &lt;code&gt;UnitOfWork&lt;/code&gt;, representing a transaction as some business entity (which the business logic core knows about). Indeed, a transaction can be represented as some kind of business entity - after all, business logic can require atomic and non-competitive execution of a transaction. But the problem with elegant ideas is the inelegant implementation - abstract factories, reflexion, and messy handling of the methods of the adapter itself. &lt;/p&gt;

&lt;p&gt;Often these complications are dictated by the desire to work with several databases, or to be able to switch from one database to another by changing only the adapter code (and without changing the business logic). &lt;/p&gt;

&lt;p&gt;Realizing that this is too "abstract," and in general does not meet the Go Way, I derived a few limitations of our project that were supposed to simplify this task:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The service works with only one database;&lt;/li&gt;
&lt;li&gt;We use a particular library to work with the database.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The service works with only one database
&lt;/h3&gt;

&lt;p&gt;And it's PosgreSQL. Well really, how often do you switch between databases? Many people try to write some generic code to work with a generic SQL database, but does it make sense? The experience shows that switching from one SQL database to another will still force you to rework the entire project, and switching from SQL to NoSQL or vice versa is out of the question.&lt;/p&gt;

&lt;h3&gt;
  
  
  We use a particular library to work with the database
&lt;/h3&gt;

&lt;p&gt;And it is &lt;a href="https://github.com/go-pg/pg" rel="noopener noreferrer"&gt;go-pg&lt;/a&gt;. I personally really like it as a query builder (rather than an ORM) and it has good performance. It has one feature which I'll tell you about next, without which I would have struggled to implement what I had in mind. But this feature is available in other libraries too, so don't rush to rewrite your code.&lt;/p&gt;

&lt;h2&gt;
  
  
  What did we end up with
&lt;/h2&gt;

&lt;p&gt;That's why I took transactions in go-pg as the basis with a clear mind. I wanted to keep the repository method signatures clean (only the context and method call parameters), but also make the solution idiomatic from a Go perspective.&lt;/p&gt;

&lt;p&gt;Golang has an excellent tool that allows you to pass utility data that doesn't concern a particular method call, but concerns the context of an operation - &lt;code&gt;context.Context&lt;/code&gt;. Often telemetry, loggers, idempotent identifiers, and other things go there. From my point of view transaction information perfectly fits the definition of "utility data" - it's some kind of process modifier which doesn't affect the logic directly but has an indirect effect. From words to deeds!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;postgres&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;txKey&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="c"&gt;// injectTx injects transaction to context&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;injectTx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tx&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;txKey&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// extractTx extracts transaction from context&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;extractTx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tx&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;txKey&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="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;tx&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first step is to add methods to put the transaction object into the context and retrieve the transaction from the context. The methods are non-exportable, i.e. they can only be called inside the adapter. Note - a transaction from the go-pg package is used here without any wrappers or abstractions. We can afford it inside the adapter!&lt;/p&gt;

&lt;p&gt;Next, we need to teach the adapter itself (the repository) how to work with a transaction. And this is where we need a feature that we have in go-pg, but not in some other libraries, such as sqlx. This is a single interface for query methods executed by the library, both in a transaction and without it. They are &lt;code&gt;Select&lt;/code&gt;, &lt;code&gt;Insert&lt;/code&gt;, &lt;code&gt;Delete&lt;/code&gt; and others - they should have the same signature for transaction and non-transaction to be able to be taken out of the interface. If not, you have to write a wrapper. In case of go-pg both database connection object and the transaction object have method &lt;code&gt;ModelContext(c context.Context, model ...interface{}) *Query&lt;/code&gt; which we used.&lt;/p&gt;

&lt;p&gt;We got a little wrapper that checks if there is a transaction in the context. If there is - it returns &lt;code&gt;Query&lt;/code&gt; from the transaction object, and if not - it returns &lt;code&gt;Query&lt;/code&gt; from the database connection object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;postgres&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="c"&gt;// model returns query model with context with or without transaction extracted from context&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;orm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;tx&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;extractTx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tx&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&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;return&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModelContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&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;Database here is the actual structure that implements &lt;code&gt;CarRepository&lt;/code&gt;, whose methods contain SQL queries to PostgreSQL, as well as a connection (or a connection pool) to the database. It can implement more repositories if you have a lot of them.&lt;/p&gt;

&lt;p&gt;As a result, the method that reads cars from the database will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;postgres&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;GetCarsByTimePeriod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Car&lt;/span&gt;

    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
        &lt;span class="n"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"manufacture_date BETWEEN ? AND ?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
        &lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
        &lt;span class="n"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Moreover, you can use a method with or without a transaction - neither the signature nor the method itself changes. And it is the part of the service with business logic that decides whether to use a transaction or not. Let's see how this is done. &lt;/p&gt;

&lt;h2&gt;
  
  
  Transactions in Business and Business in Transactions
&lt;/h2&gt;

&lt;p&gt;The only thing left is to implement a method to create a transaction inside the adapter, which would return the context "loaded" with transaction; add this method to the interface, and call it in business logic, obtaining the context with the transaction. And then pass that context to all repository calls, and commit or rollback at the end.&lt;/p&gt;

&lt;p&gt;It sounds logical, but it's kind of messy. Maybe Go has a more elegant tool? &lt;/p&gt;

&lt;p&gt;And there is one! A closure. Let's create a method that allows us to execute the whole transaction without having to leave our seat:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;postgres&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="c"&gt;// WithinTransaction runs function within transaction&lt;/span&gt;
&lt;span class="c"&gt;//&lt;/span&gt;
&lt;span class="c"&gt;// The transaction commits when function were finished without error&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;WithinTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tFunc&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// begin transaction&lt;/span&gt;
    &lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Begin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"begin transaction: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// finalize transaction on panic, etc.&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errTx&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;errTx&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"close transaction: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errTx&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="c"&gt;// run callback&lt;/span&gt;
    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;injectTx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// if error, rollback&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errRollback&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Rollback&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;errRollback&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"rollback transaction: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errRollback&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="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c"&gt;// if no error, commit&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errCommit&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Commit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;errCommit&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"commit transaction: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errCommit&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="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The method takes a context and a function to be executed in a transaction. Based on the context, a new context is created with the transaction, and passed to the function. This also allows to interrupt the execution of the function when the parent context is cancelled - for example, when a graceful shutdown occurs.&lt;/p&gt;

&lt;p&gt;Next, if the function is executed without errors, commit is executed, otherwise a rollback is executed and the error is returned from the method. &lt;/p&gt;

&lt;p&gt;We'll put this method in a separate port - isolation must be respected!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="c"&gt;// Transactor runs logic inside a single database transaction&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Transactor&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// WithinTransaction runs a function within a database transaction.&lt;/span&gt;
    &lt;span class="c"&gt;//&lt;/span&gt;
    &lt;span class="c"&gt;// Transaction is propagated in the context,&lt;/span&gt;
    &lt;span class="c"&gt;// so it is important to propagate it to underlying repositories.&lt;/span&gt;
    &lt;span class="c"&gt;// Function commits if error is nil, and rollbacks if not.&lt;/span&gt;
    &lt;span class="c"&gt;// It returns the same error.&lt;/span&gt;
    &lt;span class="n"&gt;WithinTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's add it via DI to the domain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;domain&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Car&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;carRepo&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CarRepository&lt;/span&gt;
    &lt;span class="n"&gt;transactor&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Transactor&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;NewCar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transactor&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Transactor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;carRepo&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CarRepository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Car&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;carRepo&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;carRepo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;transactor&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;transactor&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;This allows us to not worry about transactions within the business logic at all, and simplify transaction operations to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;application&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;BuyCar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;carId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ownerId&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;validateBuyer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ownerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// read owner domain&lt;/span&gt;
    &lt;span class="n"&gt;car&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;readCar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;carId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transactor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithinTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;txCtx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;car&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;txCtx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ownerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;price&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;Here, transaction management is performed within the application layer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;domain&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Sell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ownerId&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;validateBuyer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ownerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;validatePurchase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ownerId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CarEvent&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;CarId&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Seller&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Owner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Buyer&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;ownerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Price&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;SoldAt&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;carRepo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UpsertCarHistoryEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Owner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ownerId&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UpdatedAt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;carRepo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UpsertCar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"car %s model % sold to %s for %d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;car&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;car&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Note that methods &lt;code&gt;UpsertCarHistoryEvent&lt;/code&gt; and &lt;code&gt;UpsertCar&lt;/code&gt; are executed in the same transaction, even though the domain layer does not own the it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comforting results
&lt;/h2&gt;

&lt;p&gt;In the end, we got:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;simple mechanism for transaction execution in terms of the business logic layer;&lt;/li&gt;
&lt;li&gt;isolation of levels, abstractions do not leak;&lt;/li&gt;
&lt;li&gt;no reflection, all transaction work is typed and fault-tolerant;&lt;/li&gt;
&lt;li&gt;clean repository methods, no need to add a transaction to a signature;&lt;/li&gt;
&lt;li&gt;query methods are transaction agnostic - if there is a transaction, they are executed within it, if not - directly on the database;&lt;/li&gt;
&lt;li&gt;commit and rollback are executed automatically according to the function execution result. No deferring.&lt;/li&gt;
&lt;li&gt;in case of panic, rollback will be executed inside &lt;code&gt;tx.Close()&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach is applicable to any database that supports ACID transactions, as long as there is a common interface for both in-transaction and out-of-transaction queries. If you wish, you can add your own wrapper if your favorite library doesn't have it.&lt;/p&gt;

&lt;p&gt;This approach is not applicable when you work with several databases in one service and you need to link two transactions into one. In this case, I do not envy you.&lt;/p&gt;

&lt;p&gt;I may have departed from DDD principles or neglected the concepts of hexagonal architecture, but the result came out simple, beautiful and readable.&lt;/p&gt;

&lt;p&gt;What would you do? Feel free to comment for ideas and critique!&lt;/p&gt;

</description>
      <category>go</category>
      <category>architecture</category>
      <category>ddd</category>
      <category>microservices</category>
    </item>
    <item>
      <title>Practical Use of Finite-State Machines</title>
      <dc:creator>Ilya Kaznacheev</dc:creator>
      <pubDate>Mon, 29 Mar 2021 23:18:20 +0000</pubDate>
      <link>https://dev.to/ilyakaznacheev/practical-use-of-finite-state-machines-3gck</link>
      <guid>https://dev.to/ilyakaznacheev/practical-use-of-finite-state-machines-3gck</guid>
      <description>&lt;p&gt;This is the first article in a series dedicated to FSM usage in distributed system architecture. We will talk about domains, transactions, and sagas. But let's start with the basics.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finite-State Machine
&lt;/h2&gt;

&lt;p&gt;When we think about the finite-state machine, we probably imagine some computer science-related entities, math, and diagrams like that:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8egg9mj6k8b7o7yu1bh6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8egg9mj6k8b7o7yu1bh6.png" alt="Boring FSM"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Besides scientific language, a finite-state machine is a final set of states and transitions between them. When it comes to real engineering, states are a set of consistent states in which the model can be. Normally the set is not huge - having an FSM of a hundred states will cause really complex code. In my experience, it is something between three for the simplest models and 20-30 for most complex ones.&lt;/p&gt;

&lt;p&gt;Each state represents a real-world state of the model in the current moment. Important note: the state has to describe the state completely - that means you have to rely on the state field only to identify the current model state. If you need to check some extra attributes to identify the state - your FSM isn't granular enough.&lt;/p&gt;

&lt;p&gt;Transitions, in turn, are the logic of the model (or class, domain, etc.), which depends on the current state. So now everything your model is supposed to do depends on its current state - well like real-world things do.&lt;/p&gt;

&lt;h2&gt;
  
  
  FSM in real life
&lt;/h2&gt;

&lt;p&gt;You may wonder why we are talking about something that looks too scientific and forces your code in some boundaries? While you can still implement the same logic without any "weird-looking-diagrams"? That's a great question, and we will answer it.&lt;/p&gt;

&lt;p&gt;Let's look at the example. Say, we have a hand-made furniture workshop and store. So the customer orders a tailor-made table for its kitchen. We have to accomplish the following steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;accept the order;&lt;/li&gt;
&lt;li&gt;charge customers money;&lt;/li&gt;
&lt;li&gt;request the workshop to produce the table;&lt;/li&gt;
&lt;li&gt;request workers to move the table to the warehouse once it is done;&lt;/li&gt;
&lt;li&gt;order transport company to deliver the table from the warehouse to customer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It looks pretty straight-forward, but the devil is in details. You can't just do all these operations in the same API call, because of its duration. While the regular request processing time is under a second, fulfilling the order may take weeks. You can't even calculate the exact time because of the moving part amount involved.&lt;/p&gt;

&lt;p&gt;So you have to save the milestones during the order fulfillment. And by "save" I mean "store in a database". Here we are talking about the Order model.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ID&lt;/span&gt;       &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;ClientID&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;Detais&lt;/span&gt;   &lt;span class="n"&gt;Details&lt;/span&gt;
    &lt;span class="n"&gt;State&lt;/span&gt;    &lt;span class="n"&gt;State&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The simplest order has a unique ID, customer ID, order details (like delivery address, cost, positions, etc.). We add an attribute &lt;code&gt;State&lt;/code&gt; and will discuss it in detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  A process described using FSM
&lt;/h2&gt;

&lt;p&gt;The process consists of the following steps (more or less):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg1l2pw3imdlyox7zv6s3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg1l2pw3imdlyox7zv6s3.png" alt="Steps"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we can prepare a list of states for our order. They will represent each step via a related order state.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;State&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;OrderCreated&lt;/span&gt;         &lt;span class="n"&gt;State&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"order_created"&lt;/span&gt;
    &lt;span class="n"&gt;PaymentPending&lt;/span&gt;       &lt;span class="n"&gt;State&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"payment_pending"&lt;/span&gt;
    &lt;span class="n"&gt;ManufacturingPending&lt;/span&gt; &lt;span class="n"&gt;State&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"manufacturing_pending"&lt;/span&gt;
    &lt;span class="n"&gt;StockMovePending&lt;/span&gt;     &lt;span class="n"&gt;State&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"stock_pending"&lt;/span&gt;
    &lt;span class="n"&gt;DispatchPending&lt;/span&gt;      &lt;span class="n"&gt;State&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"dispatch_pending"&lt;/span&gt;
    &lt;span class="n"&gt;DeliveryPending&lt;/span&gt;      &lt;span class="n"&gt;State&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"delivery_pending"&lt;/span&gt;
    &lt;span class="n"&gt;ConfirmationPending&lt;/span&gt;  &lt;span class="n"&gt;State&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"confirmation_pending"&lt;/span&gt;
    &lt;span class="n"&gt;OrderConfirmed&lt;/span&gt;       &lt;span class="n"&gt;State&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"order_confirmed"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Note that we don't call states like &lt;code&gt;"order_accepted"&lt;/code&gt; or &lt;code&gt;"order_delivered"&lt;/code&gt;, but &lt;code&gt;"payment_pending"&lt;/code&gt; and &lt;code&gt;"confirmation_pending"&lt;/code&gt; instead. That's because &lt;code&gt;"order_accepted"&lt;/code&gt; doesn't represent the state, it represents an event in the past. We can use events to trigger state transitions, but the state has to describe the real-world state.&lt;/p&gt;

&lt;p&gt;After successful order placement, we have to charge money from the customer's bank account. And this is the current state of the order - awaiting payment, or &lt;code&gt;"payment_pending"&lt;/code&gt;. When it's done, we sent the order to the workshop and wait until it is manufactured. In this case, the state &lt;code&gt;"order_paid"&lt;/code&gt; will also be irrelevant - it is an event of successful payment, but the state is waiting till the table will be done by carpenters.&lt;/p&gt;

&lt;p&gt;So now we can go a bit nerdy and draw a state diagram.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx41yy5w29pobado3wgof.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx41yy5w29pobado3wgof.png" alt="State Diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thus, we got our process explicitly described as a finite-state machine. In fact, it is a pipeline pattern, defining which step will run in which order. But FSM allows us to clearly define validations at each step, and visibly decouple the logic of each step (or state), so you don't have to hold the whole sequence in mind while programming.&lt;/p&gt;

&lt;p&gt;Let's now dive deeper into this example.&lt;/p&gt;

&lt;h2&gt;
  
  
  More than just a pipeline
&lt;/h2&gt;

&lt;p&gt;In real life, things can sometimes go wrong. When you are developing software, there is a myriad of places, where something may go wrong. In other words, you have to handle errors and failures.&lt;/p&gt;

&lt;p&gt;It's simple to handle an error when you are processing the synchronous HTTP request. You probably can fit it into a single database transaction and just rollback any changes if an error occurs. You also can simply return an error to the HTTP client without any extra operations like cleanups, compensation transactions, etc.&lt;/p&gt;

&lt;p&gt;But when we talk about long or complex operations or actions across several distributed services, a simple approach doesn't work anymore. We need to split into steps, and steps may also include some sub-steps and so on. But how to handle errors in the middle of the pipeline? You can't just return an error message to the user because you now have some mess to clean up. Let's take a look.&lt;/p&gt;

&lt;p&gt;In a workshop, many things may go unexpected. Carpenter may take a sick leave or even resign, rare wood may be out of stock, material delivery may delay, and so on. Anyway, there are real cases, when the workshop can't fulfill the order when it is already paid.&lt;/p&gt;

&lt;p&gt;So now we have to handle an error in the middle of the process. There may be different kinds of errors or the same error, but the handling will depend on the model state. &lt;/p&gt;

&lt;p&gt;If the error occurs when the state is &lt;code&gt;"manufacturing_pending"&lt;/code&gt;, that means the workshop can't fulfill the order, and you have to refund the money.&lt;/p&gt;

&lt;p&gt;There will be new states also.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;RefundPending&lt;/span&gt;  &lt;span class="n"&gt;State&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"refund_pending"&lt;/span&gt;
    &lt;span class="n"&gt;OrderCancelled&lt;/span&gt; &lt;span class="n"&gt;State&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"order_cancelled"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The state diagram will now look like this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbujoapyc5lw9disoic2f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbujoapyc5lw9disoic2f.png" alt="State Diagram with Errors"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Consistent error handling
&lt;/h2&gt;

&lt;p&gt;The more errors that need to be processed, the more complex the state diagram becomes. But thanks to the FSM, the processing always corresponds to the current state, and the actions taken will always be appropriate.&lt;/p&gt;

&lt;p&gt;If an error (or other events) occurs, actions depend primarily on the current state of the model. Next, the transition conditions are checked, and only then the transition logic is executed. Thus, the processing of any events occurring to the entity will always be consistent with its state. &lt;/p&gt;

&lt;p&gt;This is what the complete process might look like, with all sorts of errors taken into account.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fihrh578na2h3z85z9xx5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fihrh578na2h3z85z9xx5.png" alt="Full State Diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It may look similar to the saga pattern for those, who have dealt with distributed transactions, and it actually does. But I will tell you about more advanced FSM applications in the following posts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical Advice
&lt;/h2&gt;

&lt;p&gt;Use the FSM to describe states and transitions between them. This will help to clarify and separate the logic of the different states into distinct modules. Try to describe the states in a granular way. Don't be afraid of a large number of states if the data model can actually be in each of them. Have fun drawing diagrams.&lt;/p&gt;

</description>
      <category>transaction</category>
      <category>architecture</category>
      <category>golang</category>
      <category>fsm</category>
    </item>
    <item>
      <title>Go-Swagger Tricks. Standard HTTP Handler</title>
      <dc:creator>Ilya Kaznacheev</dc:creator>
      <pubDate>Wed, 29 Jul 2020 16:15:27 +0000</pubDate>
      <link>https://dev.to/ilyakaznacheev/go-swagger-tricks-standard-http-handler-40oe</link>
      <guid>https://dev.to/ilyakaznacheev/go-swagger-tricks-standard-http-handler-40oe</guid>
      <description>&lt;p&gt;&lt;em&gt;picture © &lt;a href="https://pixabay.com/ru/users/moise_theodor-2099584/" rel="noopener noreferrer"&gt;moise_theodor&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Hey, reader! Maybe you had tried &lt;code&gt;go-swagger&lt;/code&gt; library so far. If yes, you may notice that sometimes it's not so easy to use. And it may look a bit complicated to start using it.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/go-swagger" rel="noopener noreferrer"&gt;
        go-swagger
      &lt;/a&gt; / &lt;a href="https://github.com/go-swagger/go-swagger" rel="noopener noreferrer"&gt;
        go-swagger
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Swagger 2.0 implementation for go
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;In this number of small articles, I will share my experience on how to make go-swagger more friendly. Let's start.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling requests in go-swagger
&lt;/h2&gt;

&lt;p&gt;By default, go-swagger generates a specific handler type for each endpoint in your Swagger scheme - that is, you will get a code-generated structure with all import parameters kindly parsed for you and a number of responders for each response type (with pre-generated structures as well).&lt;/p&gt;

&lt;p&gt;So usually it's ok, because you don't have to parse JSON requests by yourself and can never mind the response data marshaling - the go-swagger framework will handle all that stuff for you. But sometimes you just want to use a standard &lt;code&gt;net/http&lt;/code&gt; compatible handler with some endpoint - maybe some library provides that for you (like Prometheus metrics endpoint or Kubernetes probes). Or maybe you have some HTTP libraries written in your organization - e.g. for custom authentication and so on. &lt;/p&gt;

&lt;p&gt;So you can't do that in vanilla &lt;code&gt;go-swagger&lt;/code&gt; - each generated handler function requires an exact handler to be assigned. Let's say we want to expose a Prometheus metrics endpoint, so for the spec like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;/metrics&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;instruments&lt;/span&gt;
      &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Prometheus&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;metrics"&lt;/span&gt;
      &lt;span class="na"&gt;produces&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json"&lt;/span&gt;
      &lt;span class="na"&gt;responses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;200&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ok&lt;/span&gt;
          &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#/definitions/Any"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the generated handler function will look like that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;MetricsHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="n"&gt;instruments&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetMetricsParams&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;middleware&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Responder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// some logic here&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;instruments&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewGetMetricsOK&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
        &lt;span class="n"&gt;WithPayload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="c"&gt;// response data here&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;So is it even possible to use standard handlers? Or should I rewrite all of them in go-swagger-way?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't worry, there is no need to do extra work.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding a wrapper
&lt;/h2&gt;

&lt;p&gt;There is a way to do that by adding a tiny wrapper. We just need to find a reader and writer and put them to our &lt;code&gt;http.HandlerFunc&lt;/code&gt; function. The reader is always in the generated request structure, but the reader is provided to the generated responder object. So we have to implement a &lt;code&gt;middleware.Responder&lt;/code&gt; interface to get it. That may look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CustomResponder&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;runtime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Producer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="n"&gt;CustomResponder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;WriteResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="n"&gt;runtime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Producer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&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;And now we just need to wrap our standard handler into this wrapper and provide reader and writer as usual!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;MetricsHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="n"&gt;instruments&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetMetricsParams&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;middleware&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Responder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;CustomResponder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="n"&gt;runtime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Producer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;promhttp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Handler&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ServeHTTP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTPRequest&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;h2&gt;
  
  
  Improvements
&lt;/h2&gt;

&lt;p&gt;We can go deeper and add constructor function, which will save us a couple of lines in future:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;NewCustomResponder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;middleware&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Responder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;CustomResponder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="n"&gt;runtime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Producer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ServeHTTP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&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;and simplify our handler a bit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;MetricsHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="n"&gt;instruments&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetMetricsParams&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;middleware&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Responder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;NewCustomResponder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTPRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;promhttp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Handler&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;Sure it may look not so impressive as a distributed database internals, but it will save you some time and help to integrate a &lt;code&gt;go-swagger&lt;/code&gt; with ease.&lt;/p&gt;

&lt;p&gt;See you in the next "Go-Swagger Tricks" episode!&lt;/p&gt;

</description>
      <category>go</category>
      <category>swagger</category>
      <category>http</category>
    </item>
    <item>
      <title>How I write my unit tests in Go quickly</title>
      <dc:creator>Ilya Kaznacheev</dc:creator>
      <pubDate>Thu, 30 Apr 2020 23:47:40 +0000</pubDate>
      <link>https://dev.to/ilyakaznacheev/how-i-write-my-unit-tests-in-go-quickly-4bd5</link>
      <guid>https://dev.to/ilyakaznacheev/how-i-write-my-unit-tests-in-go-quickly-4bd5</guid>
      <description>&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://unsplash.com/@veri_ivanova"&gt;Veri Ivanova&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We all love unit tests because they help us to keep our software workable. And we all hate them because they don't appear magically - someone needs to write them. And when it comes to writing, it often takes a huge amount of time to cover the simplest cases.&lt;/p&gt;

&lt;p&gt;But I found my way to do that without pain (okay, with less pain). And I will share it with you like a simple illustrated guide. &lt;/p&gt;

&lt;h2&gt;
  
  
  Separate layers
&lt;/h2&gt;

&lt;p&gt;The principle isn't new, but it is still useful. Nowadays it comes with different names - hexagonal architecture, onion architecture, separation of concerns, etc. The main idea is that different parts (and I mean &lt;em&gt;logically&lt;/em&gt; different parts) of your application should be separated into independent parts.&lt;/p&gt;

&lt;p&gt;It's very important because you can't just test the whole app you're building. Ok, technically you can. But it will require an enormous amount of time and it will be a nightmare from a long-term perspective.&lt;/p&gt;

&lt;p&gt;Instead, make it as independent as possible. But the app or at least the microservice cannot be independent of himself! It can, and we call it &lt;strong&gt;dependency injection&lt;/strong&gt;. And it never was so easy as now in Go because we have...&lt;/p&gt;

&lt;h3&gt;
  
  
  Duck typing interfaces
&lt;/h3&gt;

&lt;p&gt;That means that if some type has the methods described in the interface, we can use it via that interface. So there is no need to do a lot of paperwork at the beginning of the project and draw a huge UMLs with all possible interactions and relationships - just write an interface for your layer with its &lt;em&gt;minimal requirements&lt;/em&gt; and pass the dependencies into it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Example: you have a business-logic package that has to save some data into a database. No need to create or get a database connection somewhere in this package - just define a &lt;code&gt;Repository&lt;/code&gt; or a &lt;code&gt;Storage&lt;/code&gt; interface (or any more suitable name) and put there all actions you want to perform with the database - save, update, read, delete, increment a counter, etc. Then you just have to put the database object, that can perform that actions - it will contain the &lt;strong&gt;exact&lt;/strong&gt; database query language code and database-specific logic. You also will handle the database connection and possible connection errors &lt;strong&gt;outside of the business-logic layer&lt;/strong&gt;. The layer will become &lt;strong&gt;independent&lt;/strong&gt; of the database layer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Inject unmanaged resources
&lt;/h3&gt;

&lt;p&gt;There are many unmanaged resources like random number generators, time, random-based hashes an so on. They don't require outside injection because they have no outside dependencies. But they still can do unpredictable impacts on test results. So instead of trying to work around them in tests, just use the same approach - inject them as an isolated dependency. Bus since they are not provided from the outside, do that from the inside!&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Example&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;NewExample&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Example&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Example&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Example&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;TimeToGo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"its time to go! %s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&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;Here you can't predict the &lt;code&gt;TimeToGo&lt;/code&gt; method response in the test - each time &lt;code&gt;time.Now()&lt;/code&gt; will return a new value. But you can take charge of it:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Example&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Time&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;NewExample&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Example&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Example&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Example&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;TimeToGo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"its time to go! %s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&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;Will work as before, but &lt;code&gt;time.Now&lt;/code&gt; is now under your control! You can easily mock it in the test:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;nowMock&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Time&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Kitchen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Kitchen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Example&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;nowMock&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TimeToGo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;"its time to go! 0000-01-01 15:04:00 +0000 UTC"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test failed"&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;So try to avoid any dependency in your logical entity. Even such a small as a current time or a random value. It's ok to hide them under interfaces or function types.&lt;/p&gt;
&lt;h2&gt;
  
  
  80/20
&lt;/h2&gt;

&lt;p&gt;100% coverage is a dream of any Open-Source developer. It's so nice to look at a green badge in your project's readme! But within productive projects, things work a different way.&lt;/p&gt;

&lt;p&gt;Normally you just don't have enough time or resources to do a 100% test coverage. End even if you'll do so, during the active development phase you will change the logic so many times, so the number of test changes will be enormous.&lt;/p&gt;

&lt;p&gt;But in fact, the 80/20 principle works here too: the 20% coverage of the "hottest" or most important code will cover 80% of the app's usage and dataflow. That means, start with the "hottest" code coverage. If you will have time and motivation you will write tests for less important parts and slowly will increase the coverage.&lt;/p&gt;

&lt;p&gt;For example, if you're building a web search service, test the search engine first. If it really works as expected, you can cover autocomplete, translation, and live preview next. But without the reliable &lt;strong&gt;core feature&lt;/strong&gt; you will fail.&lt;/p&gt;
&lt;h2&gt;
  
  
  Don't write your code
&lt;/h2&gt;

&lt;p&gt;Now you have a well isolated and pretty important part of your application ready. You need to fit your test into that frame. And thankfully your app consumes interfaces instead of exact implementations. That means, we can mock them in tests!&lt;/p&gt;

&lt;p&gt;But writing mocks is a salt mine job. We don't have to rob a robot's job - let them do what they are designed for!&lt;/p&gt;

&lt;p&gt;So I don't write test mocks, I generate them instead. To do that I use Mockery.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/vektra"&gt;
        vektra
      &lt;/a&gt; / &lt;a href="https://github.com/vektra/mockery"&gt;
        mockery
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A mock code autogenerator for Go
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;Say we have a database interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Storage&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;GetOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;CreateOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
    &lt;span class="n"&gt;DeleteOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And a code that uses it:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;AccountManager&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;storage&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Storage&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We can generate the mock simply by calling the following command:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# if the interface is in the internal/repo package&lt;/span&gt;
mockery &lt;span class="nt"&gt;-name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Storage &lt;span class="nt"&gt;-dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;internal/repo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And it will generate a perfect fitting mock for your interface! It will be stored in the &lt;code&gt;/mocks&lt;/code&gt; directory regarding the interface package directory (you can change it by specifying &lt;code&gt;-output&lt;/code&gt; parameter). Then you only need to use it in your test:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;testOrder&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;
&lt;span class="c"&gt;//...&lt;/span&gt;
&lt;span class="n"&gt;storageMock&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mocks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Storage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;storageMock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;On&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GetOrder"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"12345"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
    &lt;span class="n"&gt;Return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;testOrder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// and then inject it into your code under the test&lt;/span&gt;
&lt;span class="n"&gt;am&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;AccountManager&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;storageMock&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// execute test cases with mocked dependency ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;I normally use either the go-generate to run the mock generation:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;//go:generate mockery -name=Storage&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Storage&lt;/span&gt; &lt;span class="k"&gt;interface&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And then run &lt;code&gt;go generate ./...&lt;/code&gt;. Or I just put them as a list in the &lt;code&gt;Makefile&lt;/code&gt; with an absolute path. Or you can even generate mocks for all exported interfaces in your directory recursively. See the library details for more information.&lt;/p&gt;
&lt;h2&gt;
  
  
  Use shortcuts
&lt;/h2&gt;

&lt;p&gt;The test case writing is hard and boring work. You have to prepare a lot of data samples, do a boring work of setting the environment up, preparing infrastructure like HTTP requests and response writers, mock servers, stub data, etc. And it's really tedious to write every got and want data check and corresponding errors. &lt;/p&gt;

&lt;p&gt;But you can save some time by using shortcuts. And with shortcuts, I mean test libraries as Testify to wrap repetitive parts of tests in clean and handy helpers!&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/stretchr"&gt;
        stretchr
      &lt;/a&gt; / &lt;a href="https://github.com/stretchr/testify"&gt;
        testify
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A toolkit with common assertions and mocks that plays nicely with the standard library
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;Let's say you have a common HTTP response with something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;httptest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewRecorder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c"&gt;//... function under test call ...&lt;/span&gt;

&lt;span class="c"&gt;// assertion&lt;/span&gt;
&lt;span class="n"&gt;gotBody&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ioutil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;gotStatus&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;rr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gotBody&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bodyWant&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"wrong response body %s want %s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gotBody&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bodyWant&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;gotStatus&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;statusWant&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"wrong respose status %d want %d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gotStatus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;statusWant&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;Simple, huh? But when you have to do that 100+ times it becomes a little bit boring... You don't have to wait 'till a &lt;a href="https://en.wikipedia.org/wiki/Boreout"&gt;boreout&lt;/a&gt;! Just use shortcuts:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"github.com/stretchr/testify/assert"&lt;/span&gt;

&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;httptest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewRecorder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c"&gt;//... function under test call ...&lt;/span&gt;

&lt;span class="c"&gt;// assertion&lt;/span&gt;
&lt;span class="n"&gt;gotBody&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ioutil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;gotStatus&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;rr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt;

&lt;span class="n"&gt;assert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gotBody&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bodyWant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"wrong response body"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;assert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gotStatus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;statusWant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s"&gt;"wrong response status"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;It may be used in more interesting ways. Say, you have a method that returns a pointer and an error (a common pattern in Go). So you don't want to assert a returned value in case of error because it will cause a nil pointer to dereference. So you have to build a messy condition with a nil check and error check and so on... You don't have to:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;want&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"we want to get this exact value"&lt;/span&gt;

&lt;span class="n"&gt;got&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;GiveMeMyData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// if error is nill, NotNil will return true&lt;/span&gt;
&lt;span class="c"&gt;// otherwise it will write an error message to testing.T&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;assert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NotNil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"unexpected function error"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// here we assert the expected value&lt;/span&gt;
    &lt;span class="c"&gt;// but only if the error is nil!&lt;/span&gt;
    &lt;span class="n"&gt;assert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;got&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;want&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"unexpected function output)
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You can make in &lt;em&gt;even shorter&lt;/em&gt; by passing &lt;code&gt;testing.T&lt;/code&gt; to the assert structure:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;assert&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;assert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;/// now you don't have to pass t to each function, 500 milliseconds saved!&lt;/span&gt;
&lt;span class="n"&gt;assert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;123&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;123&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"they should be equal"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;It may look simple, but trust me, it will give you enough saved time during unit testing to drink a cup of coffee. Even to brew a coffee!&lt;/p&gt;
&lt;h2&gt;
  
  
  Make test meaningful
&lt;/h2&gt;

&lt;p&gt;I like tests, but when it comes to testing output, it getting harder to keep on track what's going on and which test was failed and why. It's ok when you have 10 or 20 test cases, but if you have hundreds of tests (or at least test sets, if you're using table tests for example) it's really hard to understand what's wrong just looking on the test output.&lt;/p&gt;

&lt;p&gt;To make it more readable you have to give a proper description. But do you &lt;strong&gt;really&lt;/strong&gt; want your test cases to be looking like this?&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;got&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;want&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"by calling a function A when there is no data in the DB table a_data and at the same time there is no incoming messages from the MQ and no idle workers in the worker pool, it returns %s but we want %s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;got&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;want&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;I hope you not. Otherwise please stop reading. Because this is my favorite part.&lt;/p&gt;

&lt;p&gt;To achieve readability in test output and make it easy to navigate you can use &lt;a href="https://en.wikipedia.org/wiki/Behavior-driven_development"&gt;BDD&lt;/a&gt; approach to testing. I use it for many reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;it helps to structure the test as a sequence of steps or a story;&lt;/li&gt;
&lt;li&gt;it's nice to read in test output because it is a complete story of your test fail;&lt;/li&gt;
&lt;li&gt;you can build your test cases as a tree from a root common input to different results;&lt;/li&gt;
&lt;li&gt;it's possible to go outside of unit tests and write a multi-step (or not) test for a real business story. So instead of testing abstract parts of your app, you can go ahead and cover a real business-process as works in real life! It's also a good way to focus on really necessary tests first because you can open a spec and write a test for it!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So let's take a closer look. For this kind of testing I use &lt;a href="https://github.com/onsi/ginkgo"&gt;ginkgo&lt;/a&gt; library. It is paired with a &lt;a href="https://github.com/onsi/gomega"&gt;gomega&lt;/a&gt; test matcher library, with a lot of test helpers and wrappers (more shortcuts!).&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/onsi"&gt;
        onsi
      &lt;/a&gt; / &lt;a href="https://github.com/onsi/ginkgo"&gt;
        ginkgo
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A Modern Testing Framework for Go
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;I'm not a fan of BDD itself, but I really like to use this approach in tests. So let me show you a couple of examples.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You need to &lt;a href="http://onsi.github.io/ginkgo/#getting-started-writing-your-first-test"&gt;set up a test suite&lt;/a&gt;. Normally I do that once in a package.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The example above may look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s"&gt;"github.com/onsi/ginkgo"&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s"&gt;"github.com/onsi/gomega"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Function A"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;When&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"I call the function"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"and there is no data in the DB table a_data"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"and there is no incoming messages from the MQ"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"and no idle workers in the worker pool"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"should return 123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="n"&gt;got&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"123"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="n"&gt;Expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;got&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"123"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                    &lt;span class="p"&gt;})&lt;/span&gt;
                    &lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"should work without errors"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"123"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="n"&gt;Expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BeNil&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;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks more like a real spec, right?&lt;/p&gt;

&lt;p&gt;And the error output will look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wIMdsHeG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/kq9z6twvrmvch6y5zc0e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wIMdsHeG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/kq9z6twvrmvch6y5zc0e.png" alt="Ginkgo test output" width="800" height="707"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also add some data into any step, and it will be added each time your test will reach this node of the test tree.&lt;/p&gt;

&lt;p&gt;A pseudocode example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Describe some test entity
├─With data X
│ ├─And with data B
│ │ └─And with data C
│ │   ├─It should do this
│ │   └─And this
│ └─But with data D
│     └─It should do this
└─With data Y
  ├─And with data E
  │   └─It should do this
  └─And with data E*
      └─It should error
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So here is a whole story, and you can describe it using ginkgo! In this case, for example, the node &lt;code&gt;And with data B&lt;/code&gt; will be executed 3 times:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;With data X -&amp;gt; &lt;strong&gt;And with data B&lt;/strong&gt; -&amp;gt; And with data C -&amp;gt; It should do this&lt;/li&gt;
&lt;li&gt;With data X -&amp;gt; &lt;strong&gt;And with data B&lt;/strong&gt; -&amp;gt; And with data C -&amp;gt; And this&lt;/li&gt;
&lt;li&gt;With data X -&amp;gt; &lt;strong&gt;And with data B&lt;/strong&gt; -&amp;gt; But with data D -&amp;gt; It should do this&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can use &lt;code&gt;BeforeEach()&lt;/code&gt; to set up some context for each step (set some variables, functions, fill mocks, and so on). And also you can use &lt;code&gt;AftrrEach()&lt;/code&gt; to cleanup at the end of the node.&lt;/p&gt;

&lt;p&gt;The library has a lot of other useful functions - &lt;code&gt;BeforeSuite&lt;/code&gt; and &lt;code&gt;AfterSuite&lt;/code&gt; and many variations to help you organize your test better.&lt;/p&gt;

&lt;p&gt;So the main idea here is to use it both as a meaningful description of the process under the test, and the test sequence and context of each step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting it all together
&lt;/h2&gt;

&lt;p&gt;So how to do it quickly? Let's summarize:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;separate layers by means of DI;&lt;/li&gt;
&lt;li&gt;use interfaces to do that;&lt;/li&gt;
&lt;li&gt;start with hottest 20% of your code;&lt;/li&gt;
&lt;li&gt;mock these interfaces with generated mocks;&lt;/li&gt;
&lt;li&gt;use shortcuts to speed-up simple tests;&lt;/li&gt;
&lt;li&gt;use the BDD test suite to describe and organize complex tests.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the end, it may look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"errors"&lt;/span&gt;
    &lt;span class="s"&gt;"temp"&lt;/span&gt;
    &lt;span class="s"&gt;"temp/mocks"&lt;/span&gt;

    &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s"&gt;"github.com/onsi/ginkgo"&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s"&gt;"github.com/onsi/gomega"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/stretchr/testify/mock"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Function A"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;storageMock&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mocks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Storage&lt;/span&gt;
        &lt;span class="n"&gt;testOrder&lt;/span&gt;   &lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;
        &lt;span class="n"&gt;testErr&lt;/span&gt;     &lt;span class="kt"&gt;error&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;BeforeEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;storageMock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mocks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Storage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;testOrder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"my test order"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;testErr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test error"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="n"&gt;When&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"we want to get some order"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"and the order exists"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;BeforeEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;storageMock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;On&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GetOrder"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"12345"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
                    &lt;span class="n"&gt;Return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;testOrder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;

            &lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"should return my test order without error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;sk&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewStorekeeper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;storageMock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;got&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetMyOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"12345"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                &lt;span class="n"&gt;Expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;got&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BeEquivalentTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"my test order"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="n"&gt;Expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BeNil&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="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"and there is no order"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;BeforeEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;storageMock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;On&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GetOrder"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Anything&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
                    &lt;span class="n"&gt;Return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;testErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;

            &lt;span class="n"&gt;It&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"should return empty order with error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;sk&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewStorekeeper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;storageMock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;got&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetMyOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"12345"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                &lt;span class="n"&gt;Expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;got&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BeNil&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                &lt;span class="n"&gt;Expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;To&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HaveOccurred&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;Pretty quick to write. And so easy read in CI logs!&lt;/p&gt;

</description>
      <category>go</category>
      <category>testing</category>
      <category>bdd</category>
      <category>mocks</category>
    </item>
    <item>
      <title>Clean Configuration Management in Golang</title>
      <dc:creator>Ilya Kaznacheev</dc:creator>
      <pubDate>Sun, 15 Dec 2019 14:05:48 +0000</pubDate>
      <link>https://dev.to/ilyakaznacheev/clean-configuration-management-in-golang-1c89</link>
      <guid>https://dev.to/ilyakaznacheev/clean-configuration-management-in-golang-1c89</guid>
      <description>&lt;p&gt;&lt;em&gt;image by &lt;a href="https://www.instagram.com/alexchoffy/"&gt;alexchoffy&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There are many good approaches to handle configuration in a modern application. Now we use such things as configuration files, environment variables, command-line parameters, as well as CI configuration patterns and on-fly config file builds, remote config servers, specific mapping, and binding services and even more complex things.&lt;/p&gt;

&lt;p&gt;But the target is the same - provide the app with a configuration, which is fast to get and easy to use. But how to do that?&lt;/p&gt;

&lt;h2&gt;
  
  
  How our applications consume configurations
&lt;/h2&gt;

&lt;p&gt;As a nice developer, I always fight with implicitly. I don’t use global variables, I structure my code the readable way and may my code flow certain and clear. There is something I really don’t like - environment variables because they are the same bad as global variables. But you can handle them the right way too if you will read them once at the beginning.&lt;/p&gt;

&lt;p&gt;So the main idea is to get the application settings from where they are (file, env, remote server, etc.) and distribute them through the app, to make them available wherever they are required. Simple task, huh? But there are still so many different ways to do so. &lt;/p&gt;

&lt;h2&gt;
  
  
  Many approaches, even more tools
&lt;/h2&gt;

&lt;p&gt;So it’s pretty clear how to get configs. Each approach has it’s own best practices. But what to do next? There is no common answer.&lt;/p&gt;

&lt;p&gt;Some libraries (like &lt;a href="https://github.com/spf13/viper"&gt;viper&lt;/a&gt; store configs as a key-value set in a global area. Pretty useful, but very implicit. Within big projects sometimes you can just lose the way how one or another variable came to this state.&lt;/p&gt;

&lt;p&gt;Other libraries put everything in one bucket - environment, files, command-line options. Even there is a nice-looking structure with all config values, that still looks unclear and useless to me. Normally, the app uses either a command-line approach (CLI tools) or config file + environment approach (web services, containerized apps, etc.).&lt;/p&gt;

&lt;p&gt;There are a lot of really good libraries for command-line argument handling like &lt;a href="https://github.com/jessevdk/go-flags"&gt;go-flags&lt;/a&gt;, and I don’t think that’s there is a reason to put everything in one single tool.&lt;/p&gt;

&lt;p&gt;But both approaches have the same issue - a lot of magic inside.&lt;/p&gt;

&lt;h2&gt;
  
  
  Magic
&lt;/h2&gt;

&lt;p&gt;Go isn’t a language for everyone. It has a lot of boilerplate, the type system isn’t so much forgivable, and OOP possibilities are very limited. But there is a nice thing that worth it - it is instantly readable, it has no magic inside. And I like that.&lt;/p&gt;

&lt;p&gt;But work with configs is often tricky, in turn. Even if you define a structure instead of using an unsafe map, you still have to declare which environment variables do you use. There is no worst thing as globally used environment variables. But even if the structure contains the explicit mapping of each environment variable, it is still hard to set up the environment outside the app. Someone (it may be you, another developer, DevOps, or just someone forced to do that) needs to find a list of environment variables used, and then use them properly.&lt;/p&gt;

&lt;p&gt;Okay, they can be listed in the documentation or in the source code. But both are bad. Documentation can be outdated, and source code needs time and skill to read.&lt;/p&gt;

&lt;p&gt;In my view, the only proper way to do that - add a full list of environment variables into a help output, including descriptions, default values and other meaningful information.&lt;/p&gt;

&lt;p&gt;I haven’t found anything not overcomplicated but still useful, with explicit configuration setup and clean and informative help output, so I’ve built it on my own.&lt;/p&gt;

&lt;h2&gt;
  
  
  One way to rule them all the clean way
&lt;/h2&gt;

&lt;p&gt;So, I’ve analyzed many popular configuration management libraries and decided to build my own. Why? There a couple of reasons:&lt;/p&gt;

&lt;p&gt;handle config files and environment variables, not command-line parameters;&lt;br&gt;
no magic, explicit way to read and use configuration;&lt;br&gt;
no implicit names, all mapping should be done using tags;&lt;br&gt;
the nice and informative help output;&lt;br&gt;
easy integration with other libraries.&lt;/p&gt;

&lt;p&gt;And here we are: &lt;a href="https://github.com/ilyakaznacheev/cleanenv"&gt;cleanenv&lt;/a&gt; library is now released and proven in production.&lt;/p&gt;

&lt;p&gt;Let’s talk about its major features.&lt;/p&gt;
&lt;h3&gt;
  
  
  Explicit configuration structure
&lt;/h3&gt;

&lt;p&gt;The main idea behind the cleanenv library is to make everything explicit and clean. No global state, no encapsulated maps, no magical background updates nether any other hidden work.&lt;/p&gt;

&lt;p&gt;Everything goes clean and visible. And that’s why a structured format was chosen as a base for configuration. The structure (as deep and complex as you want) being used to parse configuration files and environment variables. It’s also a good place to write config documentation.&lt;/p&gt;
&lt;h3&gt;
  
  
  Explicit naming and options
&lt;/h3&gt;

&lt;p&gt;The next “no-magic” step is the explicitness of variable names. There are no such things like complex environment name generators based on nested structure names - you have to set names as-is, so you can easily find them using a search.&lt;/p&gt;

&lt;p&gt;For file parsing, we are using the same approach as corresponding libraries - JSON, YAML, TOML, etc.&lt;/p&gt;

&lt;p&gt;Here is an example of a simple server configuration structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;ConfigDatabase&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Port&lt;/span&gt;     &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`yml:"port" env:"PORT" env-default:"5432"`&lt;/span&gt;
    &lt;span class="n"&gt;Host&lt;/span&gt;     &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`yml:"host" env:"HOST" env-default:"localhost"`&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt;     &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`yml:"name" env:"NAME" env-default:"postgres"`&lt;/span&gt;
    &lt;span class="n"&gt;User&lt;/span&gt;     &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`yml:"user" env:"USER" env-default:"user"`&lt;/span&gt;
    &lt;span class="n"&gt;Password&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`yml:"password" env:"PASSWORD"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This structure can be used to parse YAML configuration file, and then read some data from the environment. First, it will read the file, and then try to find environment variables using names in &lt;code&gt;env&lt;/code&gt; tags. If there were no data found in file neither in the environment, the constant from the &lt;code&gt;env-default&lt;/code&gt; will be used instead.&lt;/p&gt;

&lt;p&gt;Supported data types are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;integers;&lt;/li&gt;
&lt;li&gt;floating-point numbers;&lt;/li&gt;
&lt;li&gt;strings;&lt;/li&gt;
&lt;li&gt;booleans;&lt;/li&gt;
&lt;li&gt;arrays (with customizable separator);&lt;/li&gt;
&lt;li&gt;maps (with customizable separator);&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Readable help output
&lt;/h3&gt;

&lt;p&gt;Modern containerizable applications use environment variables as the main configuration. So, the app can have up to hundreds of variables it depends on. The common problem is that the exact list of variables is often uncertain or outdated (or even worth, they are distributed through the app and being read in some unexpectable places).&lt;/p&gt;

&lt;p&gt;To fix that the cleanenv library contains the possibility to add a well-structured list of environment variables with descriptions into help output:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ilyakaznacheev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cleanenv&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;ConfigServer&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Port&lt;/span&gt;     &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`env:"PORT" env-description:"server port"`&lt;/span&gt;
    &lt;span class="n"&gt;Host&lt;/span&gt;     &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`env:"HOST" env-description:"server host"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt; &lt;span class="n"&gt;ConfigRemote&lt;/span&gt;

&lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;cleanenv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetDescription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You will get the following:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Environment variables:
  PORT  server port
  HOST  server host
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;It will help you to get the documentation synchronized with your app with no need to have extra files.&lt;/p&gt;
&lt;h3&gt;
  
  
  Integration
&lt;/h3&gt;

&lt;p&gt;Simplicity is everything. But I want not only to give a simple tool but also to make it easy to use with other codes.&lt;/p&gt;

&lt;p&gt;Now you can easily combine it with &lt;code&gt;flag&lt;/code&gt; library help function:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Port&lt;/span&gt;     &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`env:"PORT" env-description:"server port" env-default:"5432"`&lt;/span&gt;
    &lt;span class="n"&gt;Host&lt;/span&gt;     &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`env:"HOST" env-description:"server host" env-default:"localhost"`&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt;     &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`env:"NAME" env-description:"server name" env-default:"postgres"`&lt;/span&gt;
    &lt;span class="n"&gt;User&lt;/span&gt;     &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`env:"USER" env-description:"server username" env-default:"user"`&lt;/span&gt;
    &lt;span class="n"&gt;Password&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`env:"PASSWORD" env-description:"server password"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;cfg&lt;/span&gt;     &lt;span class="n"&gt;config&lt;/span&gt;
    &lt;span class="n"&gt;cfgPath&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;fset&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;flag&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewFlagSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"My app"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flag&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ContinueOnError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;fset&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StringVar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cfgPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"cfg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"path to config file"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;fset&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Usage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cleanenv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FUsage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fset&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fset&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Usage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;fset&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If you will run &lt;code&gt;go run your_app.go -h&lt;/code&gt;, the output will be:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;amp; go run your_app.go -h
Usage of My app:
  -cfg string
        path to config file

Environment variables:
  PORT string
        server port (default "5432")
  HOST string
        server host (default "localhost")
  NAME string
        server name (default "postgres")
  USER string
        server username (default "user")
  PASSWORD string
        server password
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Possibility to do more
&lt;/h3&gt;

&lt;p&gt;That’s nice, but you may need more. For example, you may want to get configuration from a remote server, or some other tool, or update them. To do so, you can use the enhancement possibilities of the library. There are some ways to write your own logic to read the data from your own source. Read more in &lt;a href="https://github.com/ilyakaznacheev/cleanenv#custom-functions"&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Conclustion
&lt;/h2&gt;

&lt;p&gt;So the &lt;a href="https://github.com/ilyakaznacheev/cleanenv"&gt;cleanenv&lt;/a&gt; library is not the key for every lock. It is definitely not a tool-for-everything. But it is designed to do a simple, clean and readable, but flexible enough if you need so.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ilyakaznacheev"&gt;
        ilyakaznacheev
      &lt;/a&gt; / &lt;a href="https://github.com/ilyakaznacheev/cleanenv"&gt;
        cleanenv
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      ✨Clean and minimalistic environment configuration reader for Golang
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;So, I’m happy if it will help you. I’m actively using it in my projects, so it is productive-proved.&lt;/p&gt;

&lt;p&gt;Also, feel free to request any features you think may be helpful.&lt;/p&gt;

&lt;p&gt;And stay clean!&lt;/p&gt;

</description>
      <category>go</category>
      <category>devops</category>
      <category>configuration</category>
    </item>
    <item>
      <title>What's wrong with godoc?</title>
      <dc:creator>Ilya Kaznacheev</dc:creator>
      <pubDate>Sun, 21 Jul 2019 05:34:36 +0000</pubDate>
      <link>https://dev.to/ilyakaznacheev/what-s-wrong-with-godoc-3319</link>
      <guid>https://dev.to/ilyakaznacheev/what-s-wrong-with-godoc-3319</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;this is a repost from my blog at &lt;a href="http://kaznacheev.me"&gt;kaznacheev.me&lt;/a&gt;. I welcome new readers and will be happy to discuss what I'm writing about&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Like many modern languages, Golang has built-in inline documentation support tool called &lt;code&gt;godoc&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;To be honest, it’s awesome. It is a really great tool, that has a real impact on the everyday coding process. At least if you use plugins with function call tips as I do. &lt;/p&gt;

&lt;p&gt;But there is the big problem of the majority Go-projects that is tightly related to &lt;code&gt;godoc&lt;/code&gt; but lays outside of it. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;godoc&lt;/code&gt; is really cool. You can document your methods and packages, paste a code snippet, that will have to highlight on the web page. Some IDE plugins will give you signature tips based on function documentation, e.g. description of parameters. &lt;/p&gt;

&lt;p&gt;Also, some code analyzers like &lt;code&gt;go vet&lt;/code&gt; will warn you if you miss a package header or global type, function or variable description comment. It disciplines you and makes sources more readable. &lt;/p&gt;

&lt;p&gt;Only pros, huh? But there is a big con, that isn’t a fault of &lt;code&gt;godoc&lt;/code&gt; itself, but many developers expect it to solve this problem.&lt;/p&gt;




&lt;h2&gt;
  
  
  The main idea escapes the attention
&lt;/h2&gt;

&lt;p&gt;The major disadvantage of &lt;code&gt;godoc&lt;/code&gt; is its declarative nature. You can write excellent documentation with code snippets and parameter description. You can write simple use-cases for your methods, you can give a rich description of each global constant and variable. You can measure your documentation coverage and strive to beat 100%. Finally, you can write a big article in the package header as many open-source projects do, and you will have great documentation. &lt;/p&gt;

&lt;p&gt;But it will be a book. An encyclopedia. &lt;em&gt;Not the manual. Not the how-to guide.&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;There are many examples of encyclopedia-like documentation, even at big and popular projects – take a look at Docker or Kubernetes, for example. They both have really well-documented features. Each aspect has its own page on the documentation portal. They even have some examples with code snippets. But if you really want to use them, if you will try to understand some corner-cases or cross-module interaction, you will rather go to SO, then search through the docks. And that’s why. &lt;/p&gt;




&lt;h2&gt;
  
  
  You don’t need a book. You need a guide
&lt;/h2&gt;

&lt;p&gt;Last two decades the way of programming has changed radically. Instead of “hackermen” in sweaters, we have now stylish smoothie-boys, which look better than their CEOs on the investment pitch. Instead of memory leakage and Y2K problems, we argue which front-end framework is better, and performance is now a taboo subject (try to ask about performance at some front-end meetup). Modern tools and languages do the majority of the low-level stuff by their own and we can focus on more abstract tasks without the need to understand how each bit flows through the system. &lt;/p&gt;

&lt;p&gt;Sure, it’s better to know the low-level stuff. It’s much better to understand, how the compiler processes your code and when it can optimize the runtime. Nice to know the technical limitations of the tool you use, to understand what to expect. &lt;/p&gt;

&lt;p&gt;But after all, you need to invest an enormous amount of time to learn all that stuff. So much, that it probably will become obsolete before you will become a master in it. And that even can’t give you a guarantee, that a deep understanding of the tool basics will reward you as much, as much time you have spent to learn it. &lt;/p&gt;

&lt;p&gt;As an example, I use Docker in my applications a lot. I understand the basics of containerization and virtualization, but to be honest, I don’t know what &lt;em&gt;exactly&lt;/em&gt; goes on in the black box. Will I make better containers if I will meticulously read all the docks and learn each technical aspect, lying under the containerization principle? Sure. Will I make better containers if I will investigate the sources and try to understand or even debug each method. Hmm... Not sure. Probably I will become an expert in the Docker architecture and some interesting solutions, but that will not make you 10x more powerful in the container configuration. You will spend so much time to get almost nothing related to your actual tasks. &lt;/p&gt;

&lt;p&gt;That leads us to the following situation: you don’t need to know how the tool functions, but you need to know how to use it in your daily work. That means, that people will be interesting rather in real use-cases and specific problem solutions than a method documentation and option description. For many tools, Stack Overflow is much useful than the official documentation. &lt;/p&gt;

&lt;p&gt;In fact, you don’t need book-like documentation. It’s nice to have when you want to check some parameters or unclear behavior. But it shouldn’t be in the first place. What you really need is use-cases. The real examples of the app or tool usage from the first steps. &lt;/p&gt;

&lt;p&gt;I’ve seen many great tools, which are really difficult to use because you simply don’t understand where to start. They have a perfect description of each function or class, but you just missing the main idea. It’s like to talk with a person with schizophasia – you understand the words but can’t get the idea of the speech. &lt;/p&gt;

&lt;p&gt;The problem of &lt;code&gt;godoc&lt;/code&gt; is that there is no place for such use-case-oriented documentation. You can’t just put a ton of examples into a package comment. At least, you can’t structure them. You need something that will give you a documentation hierarchy different from your package content tree. But the solution exists. &lt;/p&gt;




&lt;h2&gt;
  
  
  How to make your docs useful
&lt;/h2&gt;

&lt;p&gt;When a child learns to read, he doesn’t start with a class book. He starts with fairy tales. He reads funny stories about brave characters, but not definitions of the words. That’s why children can understand us even if they don’t know all the words we use. &lt;/p&gt;

&lt;p&gt;To let your users start with ease, don't give them an encyclopedia. Give them a story. Guide them from the beginning to the end. Or at least tell them where to start. &lt;/p&gt;

&lt;p&gt;Unfortunately, it isn’t so much possible with the &lt;code&gt;godoc&lt;/code&gt;. If your project is small enough, you can do that in your &lt;code&gt;README&lt;/code&gt; or in the package descriptions, but usually, it is not the case. &lt;/p&gt;

&lt;p&gt;So, what you can do? &lt;/p&gt;

&lt;h3&gt;
  
  
  Explicit entry point
&lt;/h3&gt;

&lt;p&gt;Often when you find a new package, you don’t want to do research. You ask yourself: “ok, does it fit my requirements, or I just need to search further?”. Don’t force the user to read all the docs. Help him to start using your tool right now. &lt;/p&gt;

&lt;p&gt;Can you run the simplest option with only two lines of code? Great, let’s start with that and then add new functions one-by-one. &lt;/p&gt;

&lt;p&gt;Can you run a bare module with different parameters? No more words! Let’s do that and then give the user more and more parameters with a description of how they can help to fulfill his needs. &lt;/p&gt;

&lt;p&gt;This approach is very useful. In one hand, you will help newcomers to begin using your product with ease. On the other hand, you will gather a bigger and more involved community. &lt;/p&gt;

&lt;h3&gt;
  
  
  Real use-cases and tips
&lt;/h3&gt;

&lt;p&gt;As I mentioned above, in many cases you don’t need a detailed description of the tool to solve your problem. You need just a solution to your cases. Sometimes you even not interested in the tool or library description at all, you just want it to work. &lt;/p&gt;

&lt;p&gt;And that’s what people will be looking for in your documentation. They don’t need a detailed description of every function and every parameter, they need a way to bring it to life from scratch.&lt;/p&gt;

&lt;p&gt;So just give them what they need. Describe the main idea of your package and the use-cases. Write from the point of view of usage, not functionality. Let the user read it as a story, chapter by chapter, but avoid formal encyclopedia structure. &lt;/p&gt;

&lt;p&gt;To do all of this you need something more flexible, then a &lt;code&gt;README&lt;/code&gt; file. &lt;/p&gt;

&lt;h3&gt;
  
  
  Hierarchical documentation
&lt;/h3&gt;

&lt;p&gt;To navigate resulting “story of tool X usage” with ease you need a flexible structure capable to serve both as a table of contents and as a roadmap. Here you need something like hierarchical navigation/content tree. Maybe there are other good solutions, but I find a hierarchical view most suitable for this task. There are several ways to do that without extra work: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Built-in Wiki on GitHub, Bitbucket or same hosting – they are easy to use, support markdown and requires nothing but a repository that you already have; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://readthedocs.org/"&gt;Read the Docs&lt;/a&gt; - this service is a de-facto standard for documentation. It supports versioning, so you can have documentation for each version in parallel, that can be very helpful if you want to support previous versions; &lt;/li&gt;
&lt;li&gt;Directory with documents - you can simply create a directory with a set of markdown documents and add a table of contents in you &lt;code&gt;README.md&lt;/code&gt; or same document in the root directory. The same can be done in a single document using anchors – choice of style is up to you. Check this as a &lt;a href="https://github.com/SAP/styleguides/blob/master/clean-abap/CleanABAP.md"&gt;nice example&lt;/a&gt;. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are many other options, you can even use your &lt;code&gt;godoc&lt;/code&gt; to organize something like this, but I think, better to use each tool for its main purpose. &lt;/p&gt;




&lt;h2&gt;
  
  
  So, what about godoc?
&lt;/h2&gt;

&lt;p&gt;After all, &lt;code&gt;godoc&lt;/code&gt; is still a very cool tool to write functional documentation. I really respect people who write rich and clear code documentation using built-in tools like this. &lt;/p&gt;

&lt;p&gt;Don’t forget to use it. Start your documentation with the &lt;code&gt;godoc&lt;/code&gt; and then write more complex and more usage-oriented user manuals. As a really nice example, I can give a &lt;a href="https://github.com/go-pg/pg"&gt;go-pg&lt;/a&gt; library, that has both good inline documentation and also a Wiki with examples and use-cases. &lt;/p&gt;

&lt;p&gt;It may be difficult sometimes to write a good manual for a package, so start with simple Q&amp;amp;A page, and fill it from SO and other recourses. There you will see, what causes difficulties for users and what could be misunderstood.  Do this work iteratively and listen to your community, and you will be good.&lt;/p&gt;

</description>
      <category>go</category>
      <category>godoc</category>
      <category>documentation</category>
    </item>
    <item>
      <title>A clean way to pass configs in a Go application</title>
      <dc:creator>Ilya Kaznacheev</dc:creator>
      <pubDate>Fri, 12 Jul 2019 22:37:48 +0000</pubDate>
      <link>https://dev.to/ilyakaznacheev/a-clean-way-to-pass-configs-in-a-go-application-1g64</link>
      <guid>https://dev.to/ilyakaznacheev/a-clean-way-to-pass-configs-in-a-go-application-1g64</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;this is a repost from my blog at &lt;a href="http://kaznacheev.me" rel="noopener noreferrer"&gt;kaznacheev.me&lt;/a&gt;. I welcome new readers and will be happy to discuss what I'm writing about&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are many concurrent approaches to organize application configuration nowadays.  &lt;/p&gt;

&lt;p&gt;Classic &lt;code&gt;.ini&lt;/code&gt; files, &lt;code&gt;.json&lt;/code&gt;, &lt;code&gt;.toml&lt;/code&gt;, &lt;code&gt;.yaml&lt;/code&gt;, configs, modern &lt;code&gt;.env&lt;/code&gt; and, of course, container environment. And don’t forget about CLI arguments! Am I missing something? &lt;/p&gt;

&lt;p&gt;Let me be honest, I really dislike any implicitness in interfaces. Same for the CLI of course. Any of your interfaces, whether public or internal, API or object interface, a class method or module facade – they have to cooperate fair. The contract between you and the other side should be explicit, rightful and without any notes in small text at the bottom of the page.&lt;/p&gt;

&lt;p&gt;That means, that they have to take exactly what they are asking you for and give you exactly what they promise. They shouldn’t take anything from your pocket when you turn away. They shouldn’t send your stuff after a week or two if you agreed on an immediate transaction. They can hold the inner state. Not all of them should be immutable. But you always should be able to interact clearly and fair, that means all interactions have to be visible and legitimate, i.e. you will be able to pass the data from hand to hand. Otherwise, you will lose the contract details and sooner or later you will be fooled.&lt;/p&gt;

&lt;p&gt;Any interface also has to have a single entry point, that means you pass the whole amount of data that the interface requires at once, by passing it explicitly. It can be any type of data – values, structured or serialized data, path to file to parse or a socket to connect – the main idea is to put the data at the interface call.&lt;/p&gt;

&lt;h2&gt;
  
  
  A way to clean configuration
&lt;/h2&gt;

&lt;p&gt;So the modern trend to move any meaningful settings into environment arguments according to &lt;a href="https://12factor.net/" rel="noopener noreferrer"&gt;twelve-factor app&lt;/a&gt; looks ugly to me. No, the ideas of the twelve-factor app are sweet. But the fact that I’m compelled to pass my configuration implicitly with environment variable setup (i.e. with global variables) makes me sad.&lt;/p&gt;

&lt;p&gt;So sad, that at the beginning I’ve put all my configuration at the single config file and made a separate config file for each environment (e.g. local, stage, test, prod, etc.). Normally config files are well structured, so I can group parameters depending on their use. But it was not so useful, because I needed to store several configs in the repo for each environment and update each one after configuration structure change.&lt;/p&gt;

&lt;p&gt;Then I’ve moved to combine config with CLI arguments, because I have to pass some data directly to the app, e.g. secrets, local paths, and other variable data and the data that I can’t keep in the config file. It was better because now I can pass some values that vary in different environments as a parameter and use the same configuration file for each system. And it worked pretty well, until...&lt;/p&gt;

&lt;p&gt;Containers come. And the common way to setup a container environment is to use environment variables. It’s possible, in theory, to store config files in secrets, but it isn’t useful at all.&lt;/p&gt;

&lt;p&gt;The main disadvantage of environment variables is their implicit nature. You can’t just look at the application &lt;code&gt;help&lt;/code&gt; output, or check a configuration file structure to see a variable set and structure. When your application config is based on environment variables, you should have documentation for them, and you’re getting the burden of its update and synchronization. Otherwise, the user will not be allowed to get config information, and his experience with the app will be really terrible.  &lt;/p&gt;

&lt;p&gt;It’s also hard to debug such apps. As globals, environment variables act implicitly and can affect the program behavior at any moment. It’s not a big deal when your code is small enough to fit into your memory, but when it grows, you will struggle to try to remember where you get one or another config value.&lt;/p&gt;

&lt;p&gt;But after all I have found approach, that looks workable to me and satisfies my requirements. It combines several techniques, so let me make a quick overview.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuration file
&lt;/h2&gt;

&lt;p&gt;First, we still use a configuration file. I use it for several purposes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;declare configuration structure;&lt;/li&gt;
&lt;li&gt;keep defaults for each variable;&lt;/li&gt;
&lt;li&gt;document variables or sections;&lt;/li&gt;
&lt;li&gt;provide a config example for the app users;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I prefer to store my configs in YAML files. I don’t want to start a holy war, but just will declare why is it the best for me: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;hierarchical structure - I can be as flexible in a variable organization as I want;&lt;/li&gt;
&lt;li&gt;clean markup - I don’t need any brackets or a conglomeration of symbols to speak with the parser;&lt;/li&gt;
&lt;li&gt;comments - I can provide a detailed documentation, options, limitations, examples, best practices, etc.;
&lt;/li&gt;
&lt;li&gt;rich semantic - really advanced techniques like anchors, aliases, extensions, embedding, and others. I don’t use them really often, but sometimes they are extremely helpful;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s say we have a simple config file like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="c1"&gt;# Server configurations&lt;/span&gt;
&lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;localhost"&lt;/span&gt;
  &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8000&lt;/span&gt;

&lt;span class="c1"&gt;# Database credentials&lt;/span&gt;
&lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;admin"&lt;/span&gt;
  &lt;span class="na"&gt;pass&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;super-pedro-1980"&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;To use data from a &lt;code&gt;.yml&lt;/code&gt; file in Go you need to unmarshal it into the structure like you do for JSON. &lt;/p&gt;

&lt;p&gt;The mapping looks similar to JSON:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Config&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Server&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Port&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`yaml:"port"`&lt;/span&gt;
        &lt;span class="n"&gt;Host&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`yaml:"host"`&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="s"&gt;`yaml:"server"`&lt;/span&gt;
    &lt;span class="n"&gt;Database&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Username&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`yaml:"user"`&lt;/span&gt;
        &lt;span class="n"&gt;Password&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`yaml:"pass"`&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="s"&gt;`yaml:"database"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;I use &lt;code&gt;gopkg.in/yaml.v2&lt;/code&gt; library by Cannonical to parse YAML files.&lt;/p&gt;

&lt;p&gt;You can use &lt;code&gt;yml.Unmarshal&lt;/code&gt; to parse a byte slice, but In most cases, you will work with some data provided as &lt;code&gt;io.Reader&lt;/code&gt; implementation, so I/m using decoder that reads byte stream instead of full data stored in the memory:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;

&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"config.yml"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;processError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt; &lt;span class="n"&gt;Config&lt;/span&gt;
&lt;span class="n"&gt;decoder&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;yaml&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewDecoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;decoder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;processError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&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;And that’s it. Just like any JSON file. Now you can go ahead and write your own well-documented and well-structured config file, that will also serve as an example in your repository. &lt;/p&gt;
&lt;h2&gt;
  
  
  Environment variables
&lt;/h2&gt;

&lt;p&gt;Now let’s talk about the environment variables. Usually, they scattered through the app. But there is another approach. In Go, you can assign environment variables to structure fields as well you do that for JSON, YAML, and others. It will look like this:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Config&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Server&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Port&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`envconfig:"SERVER_PORT"`&lt;/span&gt;
        &lt;span class="n"&gt;Host&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`envconfig:"SERVER_HOST"`&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;Database&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Username&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`envconfig:"DB_USERNAME"`&lt;/span&gt;
        &lt;span class="n"&gt;Password&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`envconfig:"DB_PASSWORD"`&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;The magic is in the &lt;code&gt;github.com/kelseyhightower/envconfig&lt;/code&gt; library.&lt;/p&gt;

&lt;p&gt;In a couple of lines, it retrieves environment variables and assigns them to structure fields you have defined:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt; &lt;span class="n"&gt;Config&lt;/span&gt;
&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;envconfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;processError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&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;And that’s it. Now you have all your environment variables in the same place. No need to browse through all the repo to find where you use this variable, you can simply track down the config structure, that has a single entry-point.  &lt;/p&gt;

&lt;p&gt;Also, now you have all your environment variables declared in the same place. You can just open the config structure and see a full list of envs that the app needs. The lib provides a set of &lt;code&gt;Usage&lt;/code&gt; functions with wide output possibilities, that will allow you to add the list of env. variables to help output or wherever you want. Your application’s user will appreciate that.  &lt;/p&gt;

&lt;p&gt;Now you can use your favorite way to provide the environment variables - .env file, container settings, makefile or just a shell script. It’s up to you.&lt;/p&gt;
&lt;h2&gt;
  
  
  All together
&lt;/h2&gt;

&lt;p&gt;So now let's mix them! &lt;/p&gt;

&lt;p&gt;The same structure will look like this:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Config&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Server&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Port&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`yaml:"port", envconfig:"SERVER_PORT"`&lt;/span&gt;
        &lt;span class="n"&gt;Host&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`yaml:"host", envconfig:"SERVER_HOST"`&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="s"&gt;`yaml:"server"`&lt;/span&gt;
    &lt;span class="n"&gt;Database&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Username&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`yaml:"user", envconfig:"DB_USERNAME"`&lt;/span&gt;
        &lt;span class="n"&gt;Password&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`yaml:"pass", envconfig:"DB_PASSWORD"`&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="s"&gt;`yaml:"database"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; 


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;So first I load the data from the YAML file. It also serves me as a set of default values.&lt;/p&gt;

&lt;p&gt;Then I load envs and overwrite filled fields. That is, you don’t need to take care of missing values, they will be filled from the config file.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt; &lt;span class="n"&gt;Config&lt;/span&gt;
    &lt;span class="n"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;readEnv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%+v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;processError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"config.yml"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;processError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;decoder&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;yaml&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewDecoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;decoder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;processError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&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;func&lt;/span&gt; &lt;span class="n"&gt;readEnv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;envconfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="n"&gt;processError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&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;So, there are many advantages to this approach:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;single entry point - easy to find where each config variable came from;
&lt;/li&gt;
&lt;li&gt;simple declaration via tags;
&lt;/li&gt;
&lt;li&gt;structured configuration - you can group config sections depending on their usage;
&lt;/li&gt;
&lt;li&gt;declare defaults in a config file, overwrite what you need in each environment;
&lt;/li&gt;
&lt;li&gt;explicit list of environment variables used in the app;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’m not sure, that this approach covers any possible usage scenario, but it’s pretty useful and most important explicit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPD:&lt;/strong&gt; I've added all discussed technics into a small and simple configuration package called CleanEnv.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ilyakaznacheev" rel="noopener noreferrer"&gt;
        ilyakaznacheev
      &lt;/a&gt; / &lt;a href="https://github.com/ilyakaznacheev/cleanenv" rel="noopener noreferrer"&gt;
        cleanenv
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      ✨Clean and minimalistic environment configuration reader for Golang
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/ilyakaznacheev/cleanenvlogo.svg"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Filyakaznacheev%2Fcleanenvlogo.svg" alt="Clean Env"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Clean Env&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;Minimalistic configuration reader&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/avelino/awesome-go" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/d8b2bde4796b67266f07c7a619f554c926ca4750d5d8861b4b740baaddc3fd1e/68747470733a2f2f617765736f6d652e72652f6d656e74696f6e65642d62616467652e737667" alt="Mentioned in Awesome Go"&gt;&lt;/a&gt;
&lt;a href="https://godoc.org/github.com/ilyakaznacheev/cleanenv" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/6f6ec456dccdc50bf138899914b3fec5d57a0f0c5d8bf23659cff1218de72146/68747470733a2f2f676f646f632e6f72672f6769746875622e636f6d2f696c79616b617a6e6163686565762f636c65616e656e763f7374617475732e737667" alt="GoDoc"&gt;&lt;/a&gt;
&lt;a href="https://goreportcard.com/report/github.com/ilyakaznacheev/cleanenv" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/f6bc0fc1c58aa35774ddf1fe9dea26ee585151ea9de8d166ae87dac33e73b2dd/68747470733a2f2f676f7265706f7274636172642e636f6d2f62616467652f6769746875622e636f6d2f696c79616b617a6e6163686565762f636c65616e656e76" alt="Go Report Card"&gt;&lt;/a&gt;
&lt;a href="https://codecov.io/gh/ilyakaznacheev/cleanenv" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/1b49a1a7a329b166186e06a10efa6effd07856b29ffddbc03ed847a42fe4b89b/68747470733a2f2f636f6465636f762e696f2f6769746875622f696c79616b617a6e6163686565762f636c65616e656e762f636f7665726167652e7376673f6272616e63683d6d6173746572" alt="Coverage Status"&gt;&lt;/a&gt;
&lt;a href="https://github.com/ilyakaznacheev/cleanenv/actions" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/c39990aaf87d40b9f00e90e1cb77b9254fa3e14008260f7c67100c74716c4960/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f696c79616b617a6e6163686565762f636c65616e656e762f746573742e796d6c" alt="Build Status"&gt;&lt;/a&gt;
&lt;a href="https://github.com/ilyakaznacheev/cleanenv/releases/" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/b5c92ccfc456d1d4cf545d5ad46d3fb102a264128a5babf9ccd0c11be1bcd240/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f72656c656173652f696c79616b617a6e6163686565762f636c65616e656e762e737667" alt="Release"&gt;&lt;/a&gt;
&lt;a href="https://github.com/ilyakaznacheev/cleanenv/blob/master/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/b273c42a0e5e9d0d4e30c11321704a5f95c6ccfae5fdb8945c89fa764f799473/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f696c79616b617a6e6163686565762f636c65616e656e762e737667" alt="License"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Overview&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;This is a simple configuration reading tool. It just does the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;reads and parses configuration structure from the file&lt;/li&gt;
&lt;li&gt;reads and overwrites configuration structure from environment variables&lt;/li&gt;
&lt;li&gt;writes a detailed variable list to help output&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Content&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/ilyakaznacheev/cleanenv#installation" rel="noopener noreferrer"&gt;Installation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ilyakaznacheev/cleanenv#usage" rel="noopener noreferrer"&gt;Usage&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/ilyakaznacheev/cleanenv#read-configuration" rel="noopener noreferrer"&gt;Read Configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ilyakaznacheev/cleanenv#read-environment-variables-only" rel="noopener noreferrer"&gt;Read Environment Variables Only&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ilyakaznacheev/cleanenv#update-environment-variables" rel="noopener noreferrer"&gt;Update Environment Variables&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ilyakaznacheev/cleanenv#description" rel="noopener noreferrer"&gt;Description&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ilyakaznacheev/cleanenv#model-format" rel="noopener noreferrer"&gt;Model Format&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ilyakaznacheev/cleanenv#supported-types" rel="noopener noreferrer"&gt;Supported types&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ilyakaznacheev/cleanenv#custom-functions" rel="noopener noreferrer"&gt;Custom Functions&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/ilyakaznacheev/cleanenv#custom-value-setter" rel="noopener noreferrer"&gt;Custom Value Setter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ilyakaznacheev/cleanenv#custom-value-update" rel="noopener noreferrer"&gt;Custom Value Update&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ilyakaznacheev/cleanenv#supported-file-formats" rel="noopener noreferrer"&gt;Supported File Formats&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ilyakaznacheev/cleanenv#integration" rel="noopener noreferrer"&gt;Integration&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/ilyakaznacheev/cleanenv#flag" rel="noopener noreferrer"&gt;Flag&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ilyakaznacheev/cleanenv#examples" rel="noopener noreferrer"&gt;Examples&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ilyakaznacheev/cleanenv#contribution" rel="noopener noreferrer"&gt;Contribution&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ilyakaznacheev/cleanenv#thanks" rel="noopener noreferrer"&gt;Thanks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Installation&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;To install the package run&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;go get -u github.com/ilyakaznacheev/cleanenv&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Usage&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;The package is oriented to be simple in use and explicitness.&lt;/p&gt;
&lt;p&gt;The main idea is to use a structured configuration variable instead of any sort of dynamic set of configuration fields like some libraries does, to avoid unnecessary type conversions and move the configuration through the program as a simple structure, not as an object with complex behavior.&lt;/p&gt;
&lt;p&gt;There are just several actions you…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/ilyakaznacheev/cleanenv" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


</description>
      <category>go</category>
      <category>config</category>
    </item>
  </channel>
</rss>
