<?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: Niko Heikkilä</title>
    <description>The latest articles on DEV Community by Niko Heikkilä (@nikoheikkila).</description>
    <link>https://dev.to/nikoheikkila</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%2F32576%2F072c6436-666e-4510-984c-7291c3d845ac.jpg</url>
      <title>DEV Community: Niko Heikkilä</title>
      <link>https://dev.to/nikoheikkila</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nikoheikkila"/>
    <language>en</language>
    <item>
      <title>Bringing Back the Technical Excellence: Rules of Thumb for Effective Software Lifecycle Management</title>
      <dc:creator>Niko Heikkilä</dc:creator>
      <pubDate>Fri, 19 Nov 2021 07:58:16 +0000</pubDate>
      <link>https://dev.to/futurice/bringing-back-the-technical-excellence-rules-of-thumb-for-effective-software-lifecycle-management-12nc</link>
      <guid>https://dev.to/futurice/bringing-back-the-technical-excellence-rules-of-thumb-for-effective-software-lifecycle-management-12nc</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;In software, legacy code is a code that runs in production.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Don't you love the defeatist attitude of this quote? You might have stumbled upon it on many occasions. But, sadly, it is not too far from the &lt;em&gt;truth&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;There are many definitions for legacy code. I've settled to define it as the code we are afraid to change, yet we need to. The code whose developer experience converges asymptotically to zero over time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Complexity as a Driver for Legacy Code
&lt;/h2&gt;

&lt;p&gt;Without realising it, we tend to create software like we construct buildings. The hard way: layer after layer, too late to look back and improve things in retrospect. We fail to keep software &lt;em&gt;soft&lt;/em&gt; as we lock the details in place with complexity in both design and implementation.&lt;/p&gt;

&lt;p&gt;Complex design often comes directly from complex business requirements. We might rewrite a service &lt;em&gt;en masse&lt;/em&gt; without criticism only because the customer asked us to do so. We are keen to ask what kind of software they want us to deliver while the more pressing question is: what problem do they want us to solve?&lt;/p&gt;

&lt;p&gt;Complex implementation, therefore, is a direct consequence of complex design. However, the effects of complex implementations become more detrimental via their ability to feed themselves and keep on growing during the software lifecycle.&lt;/p&gt;

&lt;p&gt;The common culprits for complex implementations are the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  teams are not abiding by the technical excellence and software craftspersonship — also known as having &lt;strong&gt;immature senior engineers&lt;/strong&gt; in the team&lt;/li&gt;
&lt;li&gt;  too high (100 %) utilisation rates keeping everyone as busy as possible therefore stifling innovation&lt;/li&gt;
&lt;li&gt;  severe blindness towards the wastes in the development process&lt;/li&gt;
&lt;li&gt;  allowing the unplanned work (e.g. production incidents) to stall the team's throughput by holding planned work hostage&lt;/li&gt;
&lt;li&gt;  working through tasks in isolation (one-person silos) and not sharing and integrating code daily&lt;/li&gt;
&lt;li&gt;  accepting too much work, and as a result, cutting corners under pressure to get tasks done&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of the above contribute to unhealthy codebases, but the fastest way is to cut corners regularly.&lt;/p&gt;

&lt;p&gt;Often, we have a situation where our time and budget constraints seemingly do not allow us to deliver our best software craftspersonship. Unfortunately, cutting corners under pressure from stakeholders is a temptation many of us are not prepared to resist.&lt;/p&gt;

&lt;p&gt;As a result of cutting corners, our codebases typically lack automated tests, technical documentation, and sometimes even styling. So, you may only wonder, what could be the reason for not even having the chance to add a style guide to the project. Time? Carelessness? Lack of skills? All of it?&lt;/p&gt;

&lt;p&gt;The above are the definitive signs of a legacy codebase. But why should we care about legacy code?&lt;/p&gt;

&lt;p&gt;Because most legacy codebases still deliver heaps of value to their users. However, what they lack is &lt;strong&gt;cost-efficiency&lt;/strong&gt;. The cost of developing new features has risen to extreme heights due to emerging complexity. What took three hours to implement a year ago might now take three or more days, especially if the team compositions have been changing in-between and silent (undocumented) knowledge runs wild.&lt;/p&gt;




&lt;p&gt;Having worked in software lifecycle management for a significant portion of my career, I have gathered a set of practices to fight back the costs emerging from complexity.&lt;/p&gt;

&lt;p&gt;In this post, I present a brief — constantly evolving — guide on gradually bringing back technical excellence and driving down development costs.&lt;/p&gt;

&lt;p&gt;I will focus mainly on writing tests and documentation, the two most crucial things regarding missing technical excellence. Other important factors like increasing security, observability, and accessibility of a product are not discussed in this guide (yet).&lt;/p&gt;

&lt;h2&gt;
  
  
  Increasing Cost-Efficiency Through Automated Tests
&lt;/h2&gt;

&lt;p&gt;When investigating an insufficient test coverage, it's easy to let the &lt;a href="https://thedecisionlab.com/biases/the-sunk-cost-fallacy/"&gt;&lt;strong&gt;Sunken Cost Fallacy&lt;/strong&gt;&lt;/a&gt; tell us that we shouldn't bother with automated tests at &lt;em&gt;this point&lt;/em&gt; in the project.&lt;/p&gt;

&lt;p&gt;We judge that the costs of covering the &lt;em&gt;entire&lt;/em&gt; codebase with tests are already too high and thus not worth the investment. Besides, the people who originally developed the application should have paid attention to writing tests from day one.&lt;/p&gt;

&lt;p&gt;How do we fix the situation? I've found it helpful to traverse the &lt;a href="https://martinfowler.com/articles/practical-test-pyramid.html"&gt;&lt;strong&gt;Test Pyramid&lt;/strong&gt;&lt;/a&gt; from top to bottom.&lt;/p&gt;

&lt;p&gt;We start by migrating our &lt;em&gt;definition of releasable&lt;/em&gt; through automated end-to-end tests that usually test the user interface flows. Next, we sink into delivering new features, fixing defects, and refactoring code by leveraging fine-grained tests and test-driven development. Ultimately, this guides us to build coherent technical documentation through well-defined test suites.&lt;/p&gt;

&lt;h3&gt;
  
  
  End-to-End Tests
&lt;/h3&gt;

&lt;p&gt;When the codebase has zero automated tests, it usually means that all the acceptance testing before a release is done manually.&lt;/p&gt;

&lt;p&gt;It is not unusual to manage test cases in software like &lt;em&gt;Jira&lt;/em&gt; or &lt;em&gt;PractiTest&lt;/em&gt;. Within these tools, a special QA team designs and runs the tests, compare the actual test outputs to expected, and notifies the release team of any critical issues they uncovered.&lt;/p&gt;

&lt;p&gt;Meanwhile, developers are watching Youtube waiting for the bug reports to arrive.&lt;/p&gt;

&lt;p&gt;If we need to manually verify that our user interfaces are functioning correctly, the automation of said test cases is easy. Instead of clicking through pages in the browser, we can add these steps as scripts using tools like &lt;strong&gt;Cypress&lt;/strong&gt; and &lt;strong&gt;Playwright&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Yes, but... our product doesn't contain a browser interface. We have a set of legacy APIs instead.&lt;/p&gt;

&lt;p&gt;Suppose we want to verify that our backend integrations return the expected responses to given requests, it is helpful to use a technique called &lt;a href="https://blog.thecodewhisperer.com/permalink/surviving-legacy-code-with-golden-master-and-sampling"&gt;&lt;strong&gt;Golden Master&lt;/strong&gt;&lt;/a&gt;, also known as characterisation testing. If you have ever used snapshots when verifying that the shape of a complex object or a structure of a UI component did not change, you are familiar with the Golden Master technique.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Rather than revert to the Guru Checks Output antipattern, however, I take a snapshot of the last-known acceptable output — I call that the &lt;em&gt;golden master&lt;/em&gt; — and save it for future use. When I run the test again, I compare the output to the golden master, and if they match, then the test passes; if they don't match, then the test fails." — J.B. Rainsberger.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Yes, but... how do I sell the initiative of adding tests? It must cost us a fortune to have developers study automated testing techniques and then migrate our specialised test cases.&lt;/p&gt;

&lt;p&gt;Fortunately, expenses are modest if we carry out the migration in tiny steps.&lt;/p&gt;

&lt;p&gt;We can order our test cases by priority. Users being able to sign in to a self-service portal is naturally a higher priority than the business contact information being rendered in a specified order. The stakeholders are happy to know that by automation, we have eliminated the chance of human error.&lt;/p&gt;

&lt;p&gt;Alternatively, we can find out what test cases consume the most of our time. For example, clicking through the whole e-commerce flow from adding products to a shopping cart, filling in details, choosing a payment method, and eventually checking from the confirmation page that the order was processed is a significant time sink. We should record these steps and automate them. The stakeholders are happy to know that we have eliminated the time cost of manual testing and saved additional bandwidth for solving other problems by automation.&lt;/p&gt;

&lt;p&gt;Yes, but... now that we are automating everything, does it mean I have to fire our QA team?&lt;/p&gt;

&lt;p&gt;No, you don't need to fire your testing specialists. Instead, they can free up their bandwidth and expertise from mundane regression checks before releases to continuous, investigative, and exploratory testing through automation.&lt;/p&gt;

&lt;p&gt;Each &lt;em&gt;sprint&lt;/em&gt; should include a feasible amount of test case migrations. When planning the next sprint's contents, everyone must understand that our velocity drops slightly because we will not deliver as many new features right &lt;em&gt;now&lt;/em&gt;. However, after a few sprints, the automation benefits kick in, and the velocity restores to normal levels and even higher.&lt;/p&gt;

&lt;p&gt;Having a stable suite of automated end-to-end tests gives us a preliminary safety net. It guards us against breaking the whole application while we are changing it.&lt;/p&gt;

&lt;p&gt;However, refactoring and thus keeping the codebase in prime health is still potentially dangerous. For that, we need to dig deeper...&lt;/p&gt;

&lt;h3&gt;
  
  
  Fine-Grained Tests
&lt;/h3&gt;

&lt;p&gt;At this point, your most valuable revenue flows are covered with high-level end-to-end tests. The problem is, when one of these high-level tests fail, there can be multiple causes for it.&lt;/p&gt;

&lt;p&gt;When high-level end-to-end and integration tests fail, it's similar to a fire alarm buzzing you that something is burning somewhere in your city, but you don't know what it is. Alas, the only way to find out is to follow the smoke.&lt;/p&gt;

&lt;p&gt;Perhaps the worst trait of end-to-end tests is that they are slow. When a test run takes a long to complete, I'm incentivised to skip running tests frequently. When I don't run tests for each tiny change, I risk adding more and more risky changes into the batch, taking away my chance to safely change the codebase.&lt;/p&gt;

&lt;p&gt;Enter fine-grained tests, more often referred to as &lt;em&gt;unit tests&lt;/em&gt;. Practical unit tests verify &lt;strong&gt;a single behaviour of the system under test&lt;/strong&gt;. Thus, they fail for &lt;strong&gt;one and only one&lt;/strong&gt; reason.&lt;/p&gt;

&lt;p&gt;Furthermore, we can run unit tests fast side-by-side with code changes using a watchdog functionality. Whenever I write new code, tests related to my changes are run automatically. Thus, I get instant feedback telling me if I'm straying from a safe path. It's precious while refactoring: if my changes pass the tests, I commit them and carry on — otherwise, I revert my codebase back to the last working state.&lt;/p&gt;

&lt;p&gt;Yes, but... we don't have any unit tests in our codebase right now. Where should we start?&lt;/p&gt;

&lt;p&gt;Sprinkling unit tests here and there in the codebase doesn't motivate us. It's best to focus on adding tests to parts of the codebase you have changed frequently and recently (&lt;em&gt;frecency&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;We write an expectation for every new feature, witness it fail for the right reason, and finally write enough code until the expectation is satisfied.&lt;/p&gt;

&lt;p&gt;For every new defect raised, we should write an expectation exposing the fault and then write code until the expectation is satisfied and the defect has vanished.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test-Driven Development and Refactoring
&lt;/h3&gt;

&lt;p&gt;The technique I described above, also known as &lt;strong&gt;test-driven development&lt;/strong&gt;, is the easiest and safest way to introduce new features to our codebase in a controlled manner. You are free to disagree with me, but I dare you to find me a more effortless and safer way.&lt;/p&gt;

&lt;p&gt;Unfortunately, many developers still take a step backwards and judge you when they hear the acronym TDD. They may decry it as a mantra, or something sinister practised within a religious cult. To those people, we can tell that programming is always about setting expectations for your code. Without TDD, these expectations are stored in your head and verified with your human senses. With TDD, the expectations are held as executable tests and confirmed by the computer. Do you really think the human brain is more effective?&lt;/p&gt;

&lt;p&gt;That being said, TDD is not a silver bullet for every situation. Nevertheless, I like &lt;a href="https://kentcdodds.com/blog/when-i-follow-tdd"&gt;the points&lt;/a&gt; &lt;strong&gt;Kent C. Dodds&lt;/strong&gt; writes on their blog post. Precisely, I embrace the notion of not using TDD while doing exploratory coding.&lt;/p&gt;

&lt;p&gt;I, too, have found out that when I'm trying to figure out how a library or a framework works, I tend to hack some code together and learn it through exploring. Hacking is not an issue unless every task you work on becomes a feeble attempt at exploratory coding. Such is prone to happen when we don't split nor refine our tasks to concrete implementation details. The more I need to duct tape my code, the less incentivised I'm to write tests first, and my main driver is simply to make the code work.&lt;/p&gt;

&lt;p&gt;Let's remember that TDD is not a testing tool. Instead, it forces us to design our code before writing it. If we never pay attention to good design, our codebase will eventually become a rotten cavity that only gets worse before a dentist needs to operate the whole tooth with a root canal, or in our case, rewrite the code.&lt;/p&gt;

&lt;p&gt;Yes, but... using TDD, we can't write tests for the existing code by definition, right?&lt;/p&gt;

&lt;p&gt;When we are writing tests for the existing production code, we often find it difficult. As a result, our design likely contains serious flaws, usually caused by a lack of continuous refactoring, which has not played a significant role due to a lack of tests as a safety net.&lt;/p&gt;

&lt;p&gt;Fortunately, we can practise test-driven development also for the existing code by following the steps below.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the codebase, locate the code (&lt;em&gt;behaviour&lt;/em&gt;) you want to test&lt;/li&gt;
&lt;li&gt;Comment out the code as if it never existed in the first place&lt;/li&gt;
&lt;li&gt;Write a failing test&lt;/li&gt;
&lt;li&gt;Start restoring the commented out code line by line until the new test passes&lt;/li&gt;
&lt;li&gt;If necessary, write another failing test and make it pass&lt;/li&gt;
&lt;li&gt;Remove (do &lt;em&gt;not&lt;/em&gt; comment out) all the code not required for the tests to pass&lt;/li&gt;
&lt;li&gt;Refactor until the code satisfies your taste&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There are many good resources written about refactoring. I tend to follow &lt;a href="https://martinfowler.com/bliki/BeckDesignRules.html"&gt;the four rules of simple design&lt;/a&gt; by &lt;strong&gt;Kent Beck&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  make the tests pass&lt;/li&gt;
&lt;li&gt;  refactor until the intention is clear&lt;/li&gt;
&lt;li&gt;  remove duplication where feasible&lt;/li&gt;
&lt;li&gt;  remove everything not needed to satisfy the first three rules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More succinctly: &lt;strong&gt;test, refactor, remove, and repeat&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If the codebase is extremely convoluted as a consequence of lousy engineering, we find this process difficult. In those cases, instead of commenting out old code, we should consider writing a new module or class from scratch. Then, when we are ready, we can swap it with the old implementation helped by the interfaces defined in our type system. Doing so leaves us the possibility of quickly reverting back to the old implementation if things go wrong.&lt;/p&gt;

&lt;p&gt;Most importantly, writing tests side-by-side with both the existing and new production code takes away the reason to ask how much writing tests cost. The cost of writing tests is now embedded within the feature development.&lt;/p&gt;

&lt;p&gt;In some projects, I've seen teams creating separate tickets for writing tests and refactoring. Doing so, the developers think they get proper permission for these tasks, but they actually look as if apologising for following the quality standards of their craft.&lt;/p&gt;

&lt;p&gt;Differentiating tests and refactoring from feature development is &lt;strong&gt;a huge red flag&lt;/strong&gt; and makes us developers look unprofessional. We must never allow ourselves to think a feature is &lt;em&gt;done&lt;/em&gt; unless we have proved it with tests. Respectively, refactoring is a mandatory continuous practice, which must never require explicit permission.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Boring Part: Document All the Things
&lt;/h2&gt;

&lt;p&gt;Let's assume that through rigorous testing and refactoring efforts, our codebase is in better shape now. End-to-end tests ensure we can release safely, and fine-grained tests provide we can rapidly develop and refactor new features.&lt;/p&gt;

&lt;p&gt;What do we miss? Documentation!&lt;/p&gt;

&lt;p&gt;When studying a codebase for the first time, I often reach for three different types of documentation:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How do I install and run this application locally?&lt;/li&gt;
&lt;li&gt;What features does it have, and how do I use them?&lt;/li&gt;
&lt;li&gt;What past decisions have affected the design of this application and the way it works today?&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Read Me, Read Me!
&lt;/h3&gt;

&lt;p&gt;We should ensure we have a &lt;strong&gt;README&lt;/strong&gt; file in the project root for the first task. This is the front page of your documentation in modern version control systems, which people consult first. As a bare minimum, it should contain the following information.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  How to install the application&lt;/li&gt;
&lt;li&gt;  How to start the application&lt;/li&gt;
&lt;li&gt;  How to test the application manually&lt;/li&gt;
&lt;li&gt;  How to run the automated tests&lt;/li&gt;
&lt;li&gt;  How to deploy the application&lt;/li&gt;
&lt;li&gt;  How to contribute to the codebase&lt;/li&gt;
&lt;li&gt;  Who to ask for support in case the documentation can't provide answers&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Executable Documentation
&lt;/h3&gt;

&lt;p&gt;The second task, however, might surprise you. Having covered our codebase with automated tests, we actually have the relevant technical documentation right there, and we can even run it — aren't we the lucky ones.&lt;/p&gt;

&lt;p&gt;End-to-end tests document how the users interact with our application and how the client applications interact with our backend systems. When written clearly and concisely (see &lt;a href="https://automationpanda.com/bdd/"&gt;&lt;em&gt;behaviour-driven development&lt;/em&gt;&lt;/a&gt;), every developer can quickly grasp the basics.&lt;/p&gt;

&lt;p&gt;Fine-grained tests document how our internal interfaces are used and how public methods are called. Without fine-grained tests, we would have to read the entire source code to understand how to call its public methods, what inputs they require, and what kind of outputs they produce. By looking at well-defined tests, it becomes evident like reading a user manual.&lt;/p&gt;

&lt;p&gt;Yes, but... my excellent IDE shows enough information when I hover my cursor over the method name. So why do I need more documentation?&lt;/p&gt;

&lt;p&gt;Indeed, many state-of-the-art omnipotent god-given IDEs can also display the intent and signature of methods by hovering over their name. However, our code often contains intricate handling for edge cases and errors, which are not apparent by looking at the type signatures. It bears repeating that type systems, despite their value, never have, and will never, be a substitute for proper tests and documentation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Architectural Decision Records
&lt;/h3&gt;

&lt;p&gt;The third task is perhaps the most exciting one. Despite our best efforts, we can't know what we don't know. Even with codebases that could be a little more than a year old, we tend to find ourselves in peculiar waters.&lt;/p&gt;

&lt;p&gt;We may ask ourselves, how did this class come to be? Why is this state shared between these components? Why does our database table contain an index for this column? Why do we require an API key for all our public REST APIs?&lt;/p&gt;

&lt;p&gt;Surely we can figure out the answers to the above questions ourselves, or more traditionally, by asking other developers. However, wouldn't it be &lt;em&gt;nice&lt;/em&gt; to read the answers explained to you in plain text, ideally next to the source code?&lt;/p&gt;

&lt;p&gt;Yes, but... I can just as quickly run &lt;code&gt;git log&lt;/code&gt; and check the history from there.&lt;/p&gt;

&lt;p&gt;Stop right there! Commit messages in version control systems are great for capturing and explaining the motivation behind micro-level changes such as renaming a variable. However, they perform poorly for significant architectural changes spanning across tens or hundreds of small commits.&lt;/p&gt;

&lt;p&gt;Some developers are keenly obsessed with keeping the version control history linear to tell a logical narrative of changes between points A and B in time. I admit to doing it myself from time to time. Other developers like to squash merge entire feature branches so that one commit equals precisely one feature. In most situations, both approaches come with tradeoffs that make them more harmful than beneficial.&lt;/p&gt;

&lt;p&gt;Thus, we should use &lt;a href="https://adr.github.io"&gt;Architectural Decision Records&lt;/a&gt; instead.&lt;/p&gt;

&lt;p&gt;ADRs capture a single decision that has significant consequences towards the design and implementation of the product for the foreseeable future. Typically, they include but are not limited to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  title of the decision&lt;/li&gt;
&lt;li&gt;  status (proposed, accepted, rejected, or superseded by another ADR)&lt;/li&gt;
&lt;li&gt;  context (why did we have to discuss this?)&lt;/li&gt;
&lt;li&gt;  decision (where did our discussion lead us?)&lt;/li&gt;
&lt;li&gt;  possible consequences (what later impacts did our decision have?)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ADRs work well with internal meetings, and therefore we should set a requirement that no architecture meeting takes place unless a decision or proposal is produced.&lt;/p&gt;

&lt;p&gt;Yes, but... aren't the ADRs useless for us? I mean, who can't remember what we discussed yesterday or last week?&lt;/p&gt;

&lt;p&gt;You might feel like it now, but try telling that yourself in six months. I can assure you that when enough time has passed in a project, even the code you wrote is indistinguishable from someone else's.&lt;/p&gt;

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

&lt;p&gt;If you, like me, have worked in maintaining the software in the later stages of its lifecycle, I hope you have enjoyed this post and found pieces of wisdom in it.&lt;/p&gt;

&lt;p&gt;The bottom line is that we should not run away from legacy codebases. Granted, I can name a dozen more exciting tasks than trying to get an old application without tests or documentation up and running. However, that is not for us to decide. All we have to decide is what to do with the code that is given to us.&lt;/p&gt;

&lt;p&gt;Keep calm and carry on!&lt;/p&gt;




&lt;p&gt;&lt;small&gt;Photo by &lt;a href="https://unsplash.com/@carlevarino"&gt;Cesar Carlevarino Aragon&lt;/a&gt; on Unsplash&lt;/small&gt;&lt;/p&gt;

</description>
      <category>testing</category>
      <category>tdd</category>
      <category>productivity</category>
      <category>design</category>
    </item>
    <item>
      <title>Being a Good Developer: Tips for an Effective Code Review</title>
      <dc:creator>Niko Heikkilä</dc:creator>
      <pubDate>Sat, 02 Oct 2021 14:26:19 +0000</pubDate>
      <link>https://dev.to/futurice/being-a-good-developer-tips-for-an-effective-code-review-fpo</link>
      <guid>https://dev.to/futurice/being-a-good-developer-tips-for-an-effective-code-review-fpo</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Hey!&lt;/strong&gt; An &lt;a href="https://dev.to/nikoheikkila/being-a-good-developer-six-tips-for-a-painless-code-review-2la9"&gt;earlier revision of this post&lt;/a&gt; described how to survive the world of pull request workflows. However, since then, I've grown more convinced that asynchronous development is among the well-known root causes why software teams struggle to ship working code fast. Thus, I've decided to rewrite this post to reflect the way I currently think, enjoy!&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Many of us have been there. A software project with significant business value should ship to end-users as soon as possible. But, unfortunately, the budget has run over. Management is throwing a furious fit. Developers are doing their best to sustain and control damage by explaining that the features are being reviewed and tested, but all they need is a little more time.&lt;/p&gt;

&lt;p&gt;Rapid feature delivery is impossible when code reviews become a blocker, which they often are because of our inability to &lt;em&gt;shift left&lt;/em&gt; (move events earlier in the process instead of later).&lt;/p&gt;

&lt;p&gt;Some developers' comfort zone is buried under noise-cancelling headphones working on a feature isolated from the rest of the world. After a couple of days, the feature is "finished", and a pull request is submitted for peers to scrutinise. However, an effective code review can't occur because the changeset is made of inconceivable hunks of code totalling over &lt;strong&gt;1000 changed lines&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Feeling the insufferable pressure from management, another developer takes a quick glance at the changes, writes "looks good to me", and approves the pull request. Finally, another developer spots a couple of minor design issues and apologises for nitpicking while commenting. The reply is a famous line:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Yeah, let's fix this later when we have time."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Legacy code is born when we fail to build quality in.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;All those moments where we play a safe bet and write another page of technical debt explaining how we had a tight schedule. Surely, we can pay back the technical debt later. So in a sense, we can, but we &lt;em&gt;won't&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I've done this too many times and always felt ashamed, profoundly questioning my professionality. This post will explain how you can avoid these pitfalls and make code review a pleasant part of the development experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ask for a Review Before Committing
&lt;/h2&gt;

&lt;p&gt;The greatest thing you can do to improve your team's performance is to integrate feedback into your work early and often. In practice, this means you shouldn't delay improvements to a post-development phase.&lt;/p&gt;

&lt;p&gt;Many professional teams have been influenced too heavily by open-source development workflows fashioned by GitHub, where all the changesets must be submitted as pull requests. Certainly, pull requests have become a disastrous plague for many teams: they promise a sense of security but deliver only bottlenecks and constraints in return.&lt;/p&gt;

&lt;p&gt;Pull requests have their place in open-source development where trust doesn't exist. They are also helpful on those occasions where deployment to a temporary preview environment is needed. However, in other situations, pull requests do little to facilitate the code review experience, often making it worse.&lt;/p&gt;

&lt;p&gt;When the development happens asynchronously, developers work on their tasks and are forced to wait until their peers are available for review. Naturally, this incentivises starting new tasks before previous ones have been finished. It's a trait rooted in our mind that we should keep ourselves busy, lest a judging eye of the management might find us slacking and not working. However, the more tasks we start, the more context-switching penalties we suffer, and the slower our velocity become.&lt;/p&gt;

&lt;p&gt;Fortunately, the great minds of the &lt;em&gt;Extreme Programming&lt;/em&gt; community have had a solution to this for a long time: &lt;strong&gt;test-driven development&lt;/strong&gt; and &lt;strong&gt;pair/mob programming&lt;/strong&gt;. To achieve the most effective form of code review, we should use linters to check our code style, write the minimum amount of code to make tests pass, and finally use &lt;del&gt;a rubber-duck&lt;/del&gt; a teammate to verify our design. All this should happen at the same time, in the same space, on the same computer.&lt;/p&gt;

&lt;p&gt;I've written extensively about &lt;a href="https://dev.to/futurice/when-to-pair-program-and-when-to-go-solo-26jl"&gt;the benefits and pitfalls of pairing&lt;/a&gt;, so suffice to say here that the best code reviews take place instantly after a single line has been written.&lt;/p&gt;

&lt;p&gt;The bottom line is that &lt;strong&gt;code review doesn't need online tools&lt;/strong&gt;. Instead, discuss code face-to-face (in person or through screen sharing) and resolve any issues in real-time. Defects are best fixed when it's cheapest and fastest: before creating a commit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Split the Changes by Concern
&lt;/h2&gt;

&lt;p&gt;The unfortunate truth is that defects will occur despite our best intentions. Therefore, it's equally important to limit the scope of our changes by concern. That means working in the smallest feasible batches. These can be integrated into the codebase as independent &lt;a href="https://www.industriallogic.com/blog/whats-this-about-micro-commits/"&gt;&lt;em&gt;micro-commits&lt;/em&gt;&lt;/a&gt; without breaking a thing.&lt;/p&gt;

&lt;p&gt;Defects are much easier to find from micro-commits, and they are effortless to revert where needed. For example, I've lost count of how many times &lt;a href="https://git-scm.com/docs/git-bisect"&gt;&lt;code&gt;git bisect&lt;/code&gt;&lt;/a&gt; has saved the day because I've used micro-commits.&lt;/p&gt;

&lt;p&gt;Practising Continuous Integration dictates that we should push to the trunk at least daily — I prefer multiple times a day. However, developers sometimes reject this notion stating that their changes are too large and risky to integrate &lt;em&gt;now&lt;/em&gt;. Hence, they stash their batches in branches, allowing them to grow in size and become more challenging to review.&lt;/p&gt;

&lt;p&gt;A reinforcement loop where we are forced to write large batches because we're afraid of integrating reveals a malodorous design smell. The task you took wasn't correctly broken into subtasks, and it likely contains dependencies to other developers (or teams) work. Unfortunately, it's too late for us to fix lousy design during a code review, but we can monitor and improve for the future by limiting the scope and writing smaller user stories.&lt;/p&gt;

&lt;h2&gt;
  
  
  Invite the Right People to Review
&lt;/h2&gt;

&lt;p&gt;If you have written a fresh new algorithm for solving a computational problem, have it reviewed by someone who genuinely knows about code performance and the Big-O notation. In addition, people with impeccable CSS grid skills will serve you better when you're designing those fancy new UI changes later.&lt;/p&gt;

&lt;p&gt;Often team compositions vary greatly. Sometimes teams are built from generalists (&lt;em&gt;jacks of all trades&lt;/em&gt;) and sometimes specialists or deep experts in different areas. In the case of specialists, inviting more than one person to review is helpful. Doing so facilitates knowledge sharing and allows the team to level their specialities, guiding them to fluidly grow as generalists.&lt;/p&gt;

&lt;p&gt;Expecting more than one person for review might tempt us to walk our code through multiple rounds. However, what we need is a live mob review, again &lt;strong&gt;without tools&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example, changes to APIs handling sensitive data may require additional attention for security details. If the team has a security specialist, have them lead this code review and invite the team to learn.&lt;/p&gt;

&lt;p&gt;Likewise, the review for introducing a new design system to improve mobile responsiveness could be led by the group's front-end specialist.&lt;/p&gt;

&lt;h2&gt;
  
  
  Grab the Mentoring Opportunity
&lt;/h2&gt;

&lt;p&gt;In the past, I've used code review as a strict gatekeeping and controlling tool to prevent lousy code from entering the codebase. After all, that is its purpose, right? As a result, I've criticised some design decisions sharply without realising how my tone and message have influenced my team's work.&lt;/p&gt;

&lt;p&gt;Trust is in the heart of code review and deeming that all code is faulty unless proved otherwise will significantly damage the spirit of your team.&lt;/p&gt;

&lt;p&gt;Instead of scrutiny, we should use code review for mentoring. Instead of telling what all is wrong in the code, we should guide people to improve. Fix the defects together and allow the mutual trust to grow. Soon the same developers have a tremendous amount of self-confidence, and implicitly they contribute to a better and healthier codebase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trust the Infinitely Improvable Code
&lt;/h2&gt;

&lt;p&gt;Similarly, as an avid gatekeeper, it took me a lot of time to understand that the code written by others is not inherently faulty. The code might not meet my expectations of good design, but that's rarely an issue. All the code is &lt;em&gt;infinitely improvable&lt;/em&gt;, and our responsibility as developers is to improve it.&lt;/p&gt;

&lt;p&gt;As arguments arise in code reviews, it's important to practice humility and not push your solution too firmly. By saying that, do I mean that code review doesn't matter? No, but it's to be used wisely. Likely, the code reviewed to be acceptable now wouldn't pass the same inspection in six months.&lt;/p&gt;

&lt;p&gt;Keeping the code easy to change allows us to refactor it to better serve our purpose long after writing. There's no need to expect perfect software to emerge during a code review when we can perpetually grow it. That is what makes software &lt;em&gt;soft&lt;/em&gt;.&lt;/p&gt;




&lt;p&gt;&lt;small&gt;Photo by Alvaro Reyes on Unsplash.&lt;/small&gt;&lt;/p&gt;

</description>
      <category>codereview</category>
      <category>programming</category>
      <category>codequality</category>
      <category>github</category>
    </item>
    <item>
      <title>When to Pair Program and When to Go Solo</title>
      <dc:creator>Niko Heikkilä</dc:creator>
      <pubDate>Mon, 24 May 2021 14:58:16 +0000</pubDate>
      <link>https://dev.to/futurice/when-to-pair-program-and-when-to-go-solo-26jl</link>
      <guid>https://dev.to/futurice/when-to-pair-program-and-when-to-go-solo-26jl</guid>
      <description>&lt;p&gt;&lt;strong&gt;&lt;em&gt;Despite all the praise pair programming gets, it is not a silver bullet and we should carefully practise it to get the most benefit from it.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I recently finished reading &lt;a href="https://www.goodreads.com/book/show/57518328-practical-remote-pair-programming"&gt;&lt;em&gt;Practical Remote Pair Programming&lt;/em&gt; by &lt;strong&gt;Adrian Bolboacă&lt;/strong&gt;&lt;/a&gt;. In the description, the author promises to teach you the structure, organisation, communication, and tools for making (remote) pair programming successful in your (distributed) team.&lt;/p&gt;

&lt;p&gt;Pair programming is complicated. Despite that, I've been practising and advocating it for a while, trying to build solid habits around it. Thus, the book naturally caught my eye. Besides, today most of the programming work is remote, making the book a timely publication.&lt;/p&gt;

&lt;p&gt;In addition to describing when and how pair programming works to speed up your delivery process, it is crucial to understand situations where it might only slow you down. In this post, I will go over these situations briefly. However, I warmly recommend reading the book as well for complete insights.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; I will use the term &lt;em&gt;pairing&lt;/em&gt; a lot in this post in place of pair programming. Many pairing techniques also apply to &lt;em&gt;mobbing&lt;/em&gt; (mob programming), which means working in groups of three or more people. For beginners, I recommend starting in pairs and moving on to mobs after a while. In some texts, you might also stumble upon the term &lt;em&gt;ensemble programming&lt;/em&gt;, a friendlier name for mobbing. After all, we are not here to smack down the code like an angry mob, even though programming can be frustrating at times.&lt;/p&gt;




&lt;h2&gt;
  
  
  The (Not-)Obvious Benefits of Pairing
&lt;/h2&gt;

&lt;p&gt;Trying to convince your team to take on pairing can be even more challenging than programming in pairs itself. In this section, I will tell how and why pairing can make us better developers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pairing Is Mentoring
&lt;/h3&gt;

&lt;p&gt;When I began to build software for a living, I was introduced to new projects, their conventions and other ways of working through pairing. I'm eternally grateful to all my colleagues who have paired with me.&lt;/p&gt;

&lt;p&gt;The pairing has allowed me to grow orders of magnitude faster than being thrown to survive in a project alone because pairing is a pure form of mentoring. After a while, I too began pairing with new hires passing forth all the knowledge I had gained.&lt;/p&gt;

&lt;p&gt;As in martial arts, those with a higher rank are responsible for teaching others with a lower rank. Pairing is all about this. Therefore, the best pairs are often junior-senior pairs. The junior is ideally a new hire or otherwise unknown to the codebase or domain. Pairing with someone on your level can work, too, although it involves less mentoring and growing.&lt;/p&gt;

&lt;p&gt;The next time your team gets a new member, do not only throw entry-level tasks at them — pair with them, instead.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pairing Is a Learning Tool
&lt;/h3&gt;

&lt;p&gt;Like many others, I started my career studying computer science in study groups. The fundamental programming classes I attended always involved exercises. Often we would solve the given problems together, sitting next to the same computer sharing the same keyboard.&lt;/p&gt;

&lt;p&gt;In the book, the author references the four levels of knowledge related to a concept known as &lt;em&gt;staff liquidity&lt;/em&gt;. These describe the skills you possess about the current system or parts of it. They are as follows.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;em&gt;"I know nothing!"&lt;/em&gt; (beginner)&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;"I can run it"&lt;/em&gt; (intermediate)&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;"I can tweak and fix bugs"&lt;/em&gt; (advanced)&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;"I &lt;strong&gt;own&lt;/strong&gt; it!"&lt;/em&gt; (master)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Although you typically climb these levels independently with occasional support from your team, the journey towards mastery is fastest travelled by pairing. When you pair to learn, you will develop a solid understanding of the system, enabling you to redesign or refactor it faster and safer than working alone.&lt;/p&gt;

&lt;p&gt;The next time you need to learn a complex system or domain to manage it, pair with the one who knows it best.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pairing Is Sharing Knowledge
&lt;/h3&gt;

&lt;p&gt;The more we work alone, the more we gather silent knowledge about the inner workings of our code. It's inefficient and time-consuming to share knowledge only through documentation despite its importance for product longevity. We can conveniently build shared conventions about developing the product through pairing without having to sit in meetings or glance at massive pull requests.&lt;/p&gt;

&lt;p&gt;A well-established technique is to form pairs from people working with different parts of the system. For example, a back-end developer should pair with the front-end developer working with requests to the back-end interfaces. Successfully applying pairing techniques will tear down the invisible walls in your team.&lt;/p&gt;

&lt;p&gt;Note, however, that the shared knowledge should also be written down. Otherwise, it is only silent knowledge in your and your pair's heads.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pairing Is a Social Event
&lt;/h3&gt;

&lt;p&gt;Developers come in all sorts of flavours. Some like to do focus work being buried under music from noise-cancelling headphones. In contrast, others enjoy the company of their peers and have a chance to brainstorm complex problems together.&lt;/p&gt;

&lt;p&gt;I belong to the latter group. In fact, I often feel more stressed and anxious when working alone. These feelings have also begun to intensify during the pandemic. Being isolated in continuous long stretches has made my performance significantly weaker. Thus, I need social programming to live and thrive.&lt;/p&gt;

&lt;p&gt;Pairing and mobbing (style of pairing with three or more people) can also happen in social events organised within a community. Hackathons are a prominent example. If the employees are suffering from isolation, organising a community event is a beautiful way to build up the spirit and see new innovations grow.&lt;/p&gt;

&lt;p&gt;In the book, there is a fascinating anecdote about the social aspect of pairing:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"During a community event that I was facilitating, while the programmers were pairing, &lt;strong&gt;a CEO appeared&lt;/strong&gt;. He heard about this event and he was curious what was going on. Nobody had any idea about his role, and he joined, paired, and discussed just like any other attendee. Only after the event did I find out that he wasn't really a programmer."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Pairing is not only for developers. You can pair with designers, testers, product owners, managers and even your CEO under some circumstances. What value does your solution have if you can't explain it to your CEO, anyway?&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Mistakes Made While Pairing
&lt;/h2&gt;

&lt;p&gt;By now, you most likely noticed that you have tried pairing at least once, whether it was during your studies or junior years.&lt;/p&gt;

&lt;p&gt;The unfortunate fact is that many of us stop pairing at some point. We might blame bad prior experiences or incompetent managers who think that features are done fastest when everyone is working independently.&lt;/p&gt;

&lt;p&gt;It cannot be denied that pairing does not mean eating cake and drinking champagne from golden carafes all day. Hence, avoid these common mistakes.&lt;/p&gt;

&lt;h3&gt;
  
  
  You Should Be Pairing All Day
&lt;/h3&gt;

&lt;p&gt;Pairing all day every day can make you exhausted and emotionally zapped at the end of the day. I can usually last no longer than two hours talking and writing code at the same time. Then I need to rest for at least 15 minutes to ease the fatigue. Continuously keeping up a pace like this will more than likely grind our wellbeing and drop our interest in pairing.&lt;/p&gt;

&lt;p&gt;Doing the devil's math shows that we can split the average day of eight hours to a maximum of three to four extended sessions with short breaks in between. On paper, this sounds efficient, but it quickly proves as wasteful as any approach where developers are fully utilised. Remember, 100 % utilisation is a parking lot – or an express lane to sick leave.&lt;/p&gt;

&lt;p&gt;It's better to start by pairing for 25-30 minutes straight (in total 1-2 hours daily), pausing briefly, and rotating roles before carrying on. If the task is finished, you can rotate pairs before starting a new task. I recommend you download a &lt;strong&gt;Pomodoro application&lt;/strong&gt; or use any suitable timer to keep track of time. Rotating often keeps the spirit up as you are not stuck working with the same partner repeatedly.&lt;/p&gt;

&lt;p&gt;Efficiently managing the time spent in the pairing will eventually make you less and less tired while you get used to this new way of working. From there on, you can increase the amount of pairing slowly. I have found out that I can roughly keep on pairing 75 % of the day. The rest 25 % I reserve for a slack time where I learn, research, or work independently. Your perfect percentage may vary, but don't let it creep towards 100 %.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pairing in Hostile or Unknown Waters
&lt;/h3&gt;

&lt;p&gt;While pairing, we need to bounce ideas back and forth when trying to develop optimal solutions. Inevitably some of our clever ideas can sound utterly crazy or prove ineffective right after saying them.&lt;/p&gt;

&lt;p&gt;Therefore, pairing requires a psychologically safe space where you are not laughed at no matter what you say. Without psychological safety, you cannot bring forth your best ideas. In the worst case, you might end up silently following what others say, effectively reducing the pair to a solo effort.&lt;/p&gt;

&lt;p&gt;If there are any tensions within your team, you should resolve those first before starting to pair. Suppose no one in the group knows the other person, as is sometimes the case in fast-paced consultancy projects. In that case, you should wait until the team has passed its forming phase and is comfortable working as a unit.&lt;/p&gt;

&lt;p&gt;If the whole development organisation is on fire and people hate working with each other, then... well, just hand over your resignation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Expecting a Formal Code Review After Pairing
&lt;/h3&gt;

&lt;p&gt;Some developers accustomed to the ways of working in highly bureaucratic, process-oriented, and hierarchical cultures adamantly state that pairing is no substitute for a formal code review. It is further reasoned by saying that a third person with &lt;em&gt;fresh eyes&lt;/em&gt; is supposed to catch all the mistakes you and your pair have made.&lt;/p&gt;

&lt;p&gt;In these situations, ask how the third developer — who has no context or deep understanding of the problem at hand — could review the solution any better? Asynchronous ways of working have instilled the notion that we must always invite an outsider to scan our code. Contrary, the code review is most useful coming from peers who understand the context and problem. They are your pairs.&lt;/p&gt;

&lt;p&gt;Suppose we have a process demanding an external code review despite pairing. In the worst case, you first have to wait for the third developer to detach from their current task. Then they suffer through all the negative impacts of context switching before studying the code you have written. Finally, they present you their often flawed feedback before switching back to their previous task again. This drastically reduces the team's throughput and increases your lead times.&lt;/p&gt;

&lt;p&gt;Instead, try pairing in a continuous delivery mindset. Design, implement, review, test, and finally deploy your solution. Then make a (virtual) high-five, grab coffees, and move on to the next task. I've efficiently completed user stories with tens of subtasks without creating significant defects in a day or two doing so.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pairing With Unclear Requirements
&lt;/h3&gt;

&lt;p&gt;When your team has no clear understanding of the problem or the domain, pairing is not fruitful. You will likely end up staring at a task description — often phrased verbatim by the client or product owner — with open mouths for a couple of minutes before moving on to an easier task.&lt;/p&gt;

&lt;p&gt;Fortunately, pairing is not always about programming because solving problems is not always about writing code either. You can pair with your product owner or team lead and together write high-quality requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pairing with Bad Coding Practices
&lt;/h3&gt;

&lt;p&gt;Pairing is ineffective when tight schedules force your team to cut corners daily, resulting in technical debt. After deciding to improve the codebase quality, you can slowly start slicing through the legacy cruft and refactor the code in pairs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical Tips Before Starting Pairing
&lt;/h2&gt;

&lt;p&gt;This section shall briefly describe small tricks that have helped me whether pairing remotely or in the office.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Ensemble Commits
&lt;/h3&gt;

&lt;p&gt;During pairing, we usually share the same codebase, which means that version control tools cannot accurately determine who has done what. It is not always crucial to attribute commits to specific authors. After all, the team should have &lt;em&gt;collective ownership&lt;/em&gt; of the solution. However, it is a good practice to give credit where it is due.&lt;/p&gt;

&lt;p&gt;This can be done with a technique called &lt;em&gt;ensemble commits&lt;/em&gt;, in which you simply write the name and email of your pair in the commit message footer. See the example below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;feat(api): add a new route '/films' for fetching IMDb data

// optional longer description here

Co-authored-by: Steve McQueen &amp;lt;kingofcool@gmail.com&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not all source control providers display this information correctly. However, &lt;a href="https://docs.github.com/en/github/committing-changes-to-your-project/creating-and-editing-commits/creating-a-commit-with-multiple-authors"&gt;&lt;strong&gt;GitHub&lt;/strong&gt;&lt;/a&gt; favours this approach when specifying multiple authors. It is a great way to give credit to your pair. It also helps other developers to see who should they ask about this commit later.&lt;/p&gt;

&lt;h3&gt;
  
  
  Assign Roles
&lt;/h3&gt;

&lt;p&gt;Besides writing informative commit messages, make sure to commit early and often. You can also assign roles — minding the rotation — while pairing. Test-driven development while pairing is particularly effective when the other developer is unfamiliar with solid testing practices.&lt;/p&gt;

&lt;p&gt;In TDD pairs, the other developer writes a failing test. The other developer follows by writing the code to make the test pass. After a passing test, save the game by committing and proceed to refactor the solution. After the code is clean enough and the test is still passing, commit again. Eventually, you may squash the commits together if needed before pushing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use a Proper IDE
&lt;/h3&gt;

&lt;p&gt;This is a matter of taste, but for myself, tools like &lt;em&gt;JetBrains' Code With Me&lt;/em&gt; and &lt;em&gt;Microsoft's Visual Studio Live Share&lt;/em&gt; are among the best pairing tools. You can also share a terminal session through a multiplexer like &lt;code&gt;tmux&lt;/code&gt;. Still, I would avoid this unless my pair is perfectly comfortable swimming in the terminal and using a terminal editor like &lt;em&gt;Emacs&lt;/em&gt; or &lt;em&gt;Vim&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I have found Visual Studio Live Share the best tool for my pairing sessions. Most of the time, I share my local environment through the remote port forwarding feature. Doing so allows my pair to navigate to the same &lt;code&gt;http://localhost:&amp;lt;port&amp;gt;&lt;/code&gt; address and see the live development environment. I can also share access to my terminal when pairing, which helps them to see what commands I use in my development flow. All this is done with few clicks, and it makes remote pairing almost as frictionless as sitting together.&lt;/p&gt;

&lt;p&gt;While IDEs are geared towards remote pairing, they also work locally, given an ideal network setup and low latencies. For many, it's uncomfortable to share a keyboard (feel the germs! 🦠). Sharing an environment between two or more laptops is the better option.&lt;/p&gt;

&lt;h3&gt;
  
  
  Share the Screen for Context
&lt;/h3&gt;

&lt;p&gt;Nevertheless, do not rely solely on your IDE because sometimes you need to show your pair the precise situation. Especially when working on front-end tasks, make sure you're sharing the right browser tab so your team can see what is happening.&lt;/p&gt;

&lt;p&gt;Platforms we typically use for remote meetings like &lt;em&gt;Google Meet&lt;/em&gt;, &lt;em&gt;Zoom&lt;/em&gt;, and &lt;em&gt;Microsoft Teams&lt;/em&gt; are viable choices.&lt;/p&gt;

&lt;h3&gt;
  
  
  Invest in the Equipment
&lt;/h3&gt;

&lt;p&gt;Make sure you have a decent webcam, headphones and microphone available when pairing.&lt;/p&gt;

&lt;p&gt;Never, ever use your laptop's built-in microphone and speakers. The sound of your typing and the echo of your pairs voice will severely distract their thoughts and abruptly thrash the experience. The book walks an extra mile describing different high-end podcasting setups with proper mic stands and pop filters. Still, you can make do with affordable headsets as long as they are not the cheapest earbuds. A good rule of thumb for headphones is to try them out while pairing for an hour. If your ears hurt, change the headphones.&lt;/p&gt;

&lt;p&gt;You don't always need to have your webcam on. Sometimes it can make pairing feel more personal, but not everyone is comfortable with having cameras on all the time. Discuss this with your pair if needed before your first session. If you choose to have your webcam on, adjust the lighting so you don't end up looking like a black silhouette in front of bright daylight. Shut down any backlight sources and turn on a front light – not too bright – to make your face more visible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ensure a Stable Network Connection
&lt;/h3&gt;

&lt;p&gt;Sloppy connection causes your voice to stutter or become robotic, which is a distraction. However, you don't need 1 GB optic fibre for pairing. As long as you make sure the connection is stable, latencies are low, and there's enough extra bandwidth for audio and video. The rest of the family should not be watching too much Netflix in the other room.&lt;/p&gt;

&lt;p&gt;If you're using Wi-Fi, move as close to the router as comfortable. Plugin the Ethernet cable whenever you can. If the current task does not require using a VPN connection or other proxies, disable them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Should I Pair?
&lt;/h2&gt;

&lt;p&gt;In this post, I outlined many benefits, pitfalls and practical tips to ease your journey into pairing. However, the critical question is: when should I pair?&lt;/p&gt;

&lt;p&gt;It is not always easy to decide. In my experience, there are specific tasks that are often better solo. These include but are not limited to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;deploying builds and running scripts&lt;/li&gt;
&lt;li&gt;documenting existing features and writing simple instructions&lt;/li&gt;
&lt;li&gt;checking if a reported bug can be reproduced&lt;/li&gt;
&lt;li&gt;doing mundane and routine tasks, which should be automated, anyway&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For all other tasks that require levelling knowledge, learning new concepts, or solving challenging problems, the answer is often &lt;em&gt;yes&lt;/em&gt;. You should always be ready to pair.&lt;/p&gt;

&lt;p&gt;If you need more help to get started with pairing, read the book. You can also contact me for coaching. Let us make the development world better by working together.&lt;/p&gt;




&lt;p&gt;Photo by &lt;strong&gt;Nathan Dumlao&lt;/strong&gt; on &lt;a href="https://unsplash.com/photos/QMhc3D_zwJ0"&gt;Unsplash&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>productivity</category>
      <category>books</category>
      <category>codereview</category>
    </item>
    <item>
      <title>Health Check Before Jumping to a New Organization</title>
      <dc:creator>Niko Heikkilä</dc:creator>
      <pubDate>Mon, 10 May 2021 10:14:32 +0000</pubDate>
      <link>https://dev.to/nikoheikkila/health-check-before-jumping-to-a-new-organization-3658</link>
      <guid>https://dev.to/nikoheikkila/health-check-before-jumping-to-a-new-organization-3658</guid>
      <description>&lt;p&gt;Many software developers who have at least a couple of years of experience in this industry are often barraged with headhunting letters from recruiters. Unfortunately, I have seen too many letters sent &lt;em&gt;en masse&lt;/em&gt; vaguely describing an open position for some unnamed client followed by a generic description of a typical tech stack.&lt;/p&gt;

&lt;p&gt;What is even worse is the instant realization that the recruiter has not bothered researching who you are. Instead, you only receive shallow promises of competitive salaries, exciting projects, education budgets, and as of lately, flexible remote working opportunities.&lt;/p&gt;

&lt;p&gt;Dear recruiters, your tech stack is not important to me. All the languages and frameworks eventually suck, but they may perform well enough to solve the problem at hand given the proper context.&lt;/p&gt;

&lt;p&gt;Competitive salary and interesting projects mean nothing because every company has those. Instead, consider putting the salary range and public reference cases on your website for everyone to see.&lt;/p&gt;

&lt;p&gt;Education budgets are excellent to have, but sending people to training only to give them a break from the tedious work you make them do is a waste.&lt;/p&gt;

&lt;p&gt;Coronavirus changed the knowledge work to remote by default. The client you're hiring for is likely only waiting for the pandemic to end so they can restore the correct ways of working by forcing everyone to work at the office again.&lt;/p&gt;

&lt;p&gt;Fortunately, you can do better!&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting the Expectations Right
&lt;/h2&gt;

&lt;p&gt;To help you attract great developers and software craftspeople — like I am — here is a non-exhaustive checklist of &lt;strong&gt;DOs&lt;/strong&gt;. The emphasis is on myself and how I would like to work with you. The list contains some overlapping notions, and it is subject to change in the future because our profession is constantly evolving.&lt;/p&gt;

&lt;p&gt;The next time your head is being hunted, consider following up with the below reply. By all means, steal this list and adapt it to your taste.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Hey X.X!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reaching out. Before making any decisions, I would like to inquire about your company's (or client's) perspective to the following questions. I've found this information valuable to know when seeking new challenges. Please, take all the time required to go through these questions and get back to me. In any case, I hope we can have a fruitful discussion over these topics later.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Do&lt;/strong&gt; the &lt;a href="https://agilemanifesto.org/"&gt;Agile Manifesto&lt;/a&gt; and its &lt;a href="https://agilemanifesto.org/principles.html"&gt;12 principles&lt;/a&gt; act as a general baseline guiding your development?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do&lt;/strong&gt; your teams strive for professionalism following the &lt;a href="http://manifesto.softwarecraftsmanship.org/"&gt;Manifesto for Software Craftsmanship&lt;/a&gt;?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do&lt;/strong&gt; you grow autonomous and self-organizing teams trusting them to get the job done?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do&lt;/strong&gt; your developers mentor and cross-train each other to elevate the team's spirit, skills, and knowledge?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do&lt;/strong&gt; your teams continuously improve their craft by having agile retrospectives?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do&lt;/strong&gt; your teams solve problems efficiently using collaboration and co-creation patterns like &lt;a href="https://www.agilealliance.org/resources/experience-reports/mob-programming-agile2014/"&gt;pair and mob programming&lt;/a&gt; (also known as &lt;em&gt;swarming&lt;/em&gt;)?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do&lt;/strong&gt; your teams build quality products with &lt;a href="https://www.agilealliance.org/glossary/xp"&gt;Extreme Programming&lt;/a&gt;, &lt;a href="https://martinfowler.com/articles/continuousIntegration.html"&gt;Continuous Integration&lt;/a&gt; and &lt;a href="https://www.martinfowler.com/bliki/ContinuousDelivery.html"&gt;Continuous Delivery&lt;/a&gt; methodologies?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do&lt;/strong&gt; your teams develop and maintain software with clean object-oriented and functional programming principles?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do&lt;/strong&gt; your teams ensure software maintainability by writing automated unit, integration, and end-to-end test suites following the behaviour-driven and test-driven development practices?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do&lt;/strong&gt; your core values include customer satisfaction, trust, innovation, transparency, care, and social responsibility?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do&lt;/strong&gt; your projects make the world a better place for everyone through diversity, equity, and inclusion?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do&lt;/strong&gt; you share a transparent salary and career path policy either internally or with the public audience?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you ticked &lt;strong&gt;yes&lt;/strong&gt; to at least &lt;strong&gt;10&lt;/strong&gt; of the questions above, I'm more than happy to continue the process with you.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Should I Use This?
&lt;/h2&gt;

&lt;p&gt;It's worth knowing that this health check is most useful when you're already satisfied working for your current employer. If you are unemployed you might want to adjust it for your situation.&lt;/p&gt;

&lt;p&gt;Unfortunately, the IT recruiting world has its fair share of unethical wolfpacks (agencies) whose objective is to hunt you down and bring your head in for a reward. It bears repeating that having everything in balance is the best asset and defense when looking for new positions. It means you have not fallen into a pit of despair, and you are not easy prey for the wolves. Make your expectations and demands known.&lt;/p&gt;




&lt;p&gt;Photo by &lt;strong&gt;Thomas Bonometti&lt;/strong&gt; on &lt;a href="https://unsplash.com/photos/dtfyRuKG7UY"&gt;Unsplash&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>career</category>
      <category>recruitment</category>
      <category>agile</category>
      <category>technology</category>
    </item>
    <item>
      <title>You Can't Grow Yourself Unless You Grow Others</title>
      <dc:creator>Niko Heikkilä</dc:creator>
      <pubDate>Sat, 01 May 2021 08:24:11 +0000</pubDate>
      <link>https://dev.to/futurice/you-can-t-grow-yourself-unless-you-grow-others-3230</link>
      <guid>https://dev.to/futurice/you-can-t-grow-yourself-unless-you-grow-others-3230</guid>
      <description>&lt;p&gt;During our careers, almost everyone has regular one-on-one meetings, sometimes referred to as performance reviews. There we take time to go through our past, present, and future in the context of our jobs. In Futurice, these are called &lt;a href="https://github.com/futurice/myRetro-template/blob/master/MyRetro.md"&gt;&lt;strong&gt;MyRetros&lt;/strong&gt;&lt;/a&gt;, where my supervisor and I specifically look back on the last six months, the current state, and possible future endeavours.&lt;/p&gt;

&lt;p&gt;Often it's not enough to have a minimum of two one-on-ones per year. Given that our supervisors may have many people under their supervision, it's essential that we also grow each other in a &lt;strong&gt;peer-to-peer&lt;/strong&gt; fashion.&lt;/p&gt;

&lt;p&gt;I'm happy to have been mentored by several experienced developers during my career. Still, I'm even more delighted to take that knowledge and pass it on to my mentees. Too often, we might think that advancing in our careers requires supreme technical understanding gained only through decades of hard work. Equally important is our set of soft skills and our capability to grow others.&lt;/p&gt;

&lt;p&gt;We should set clear expectations during our performance reviews to not be promoted unless we have grown others. Our careers should have more meaning than plainly satisfying our egos and boosting our monthly salaries.&lt;/p&gt;

&lt;p&gt;In the last couple of years, I've been actively transforming myself from a regular software developer to a mentor developer. It is challenging, but doing so keeps the spark lit, giving me a direction to progress.&lt;/p&gt;

&lt;p&gt;In this post, I explain how you can transform yourself too.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Several Benefits of Mentoring
&lt;/h2&gt;

&lt;p&gt;Mentoring can sometimes be seen as a very formal relationship where you have to be a genuine senior staff member before mentoring juniors.&lt;/p&gt;

&lt;p&gt;What is often missing from the picture is that we can also mentor each other irrelevant of our current titles. Everyone who has been a part of multiple teams knows what techniques have worked in particular contexts and can carry that wisdom to future projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  You Learn Through Mentoring
&lt;/h3&gt;

&lt;p&gt;A crucial aspect of mentoring is to understand that it is not unidirectional teaching and preaching. As mentors, we must learn and improve based on the feedback we receive from our mentees. I find the traditional division of junior and senior employees holding us back, especially in mentoring.&lt;/p&gt;

&lt;p&gt;I've worked with seniors who have struggled with fundamentals and worked with juniors who have expanded my thinking with much more efficient solutions. In a sense, we are all juniors, but let's call ourselves people for the sake of equality.&lt;/p&gt;

&lt;p&gt;Like our business models should not be strictly about business-to-consumer or business-to-business, our learning should not be from seniors to juniors, but peer-to-peer.&lt;/p&gt;

&lt;h3&gt;
  
  
  You Can Level Team Competencies
&lt;/h3&gt;

&lt;p&gt;When you practice mentoring as part of your daily team routine, you end up levelling your team's competency. As a result, wide knowledge gaps occur more rarely, which is facilitated by continuous knowledge sharing.&lt;/p&gt;

&lt;p&gt;Your design-oriented frontend developer should be able to help with databases and securing backends. Respectively, the developer handling DevOps tasks should be able to help with sketching new user interfaces. Everyone in the team should be comfortable talking to the client, bringing forth new ideas and challenging the old ways of working.&lt;/p&gt;

&lt;p&gt;Furthermore, levelling the team helps to reduce the bus factor. The project doesn't halt when the integration specialist gets sick or when the business director burns out and is set aside for a couple of months. Thus, we need to build a team of solid generalists – also known as T-shaped professionals.&lt;/p&gt;

&lt;p&gt;The desired side-effect of levelling the competencies is increased autonomy. When people are comfortable working in a generalist mindset, they feel a greater sense of freedom regarding what tasks they can pick. As I mentioned in &lt;a href="https://nikoheikkila.fi/blog/the-unsurprising-truth-about-what-motivates-developers/"&gt;my earlier blog post&lt;/a&gt;, &lt;strong&gt;autonomy&lt;/strong&gt; leads to &lt;strong&gt;mastery&lt;/strong&gt; which leads to finding our &lt;strong&gt;purpose&lt;/strong&gt;. The three elements together significantly boost the retention and desire to work in your company.&lt;/p&gt;

&lt;h3&gt;
  
  
  You Contribute to a Better Hiring and Onboarding Experiences
&lt;/h3&gt;

&lt;p&gt;Mentoring begins from the very first moment I meet someone. Interviews are a great place to practice mentoring.&lt;/p&gt;

&lt;p&gt;If a developer is applying to your company either for a full-time position or a summer gig, I can mentor them. How could they improve their cover letter or resume? Did we give them a homework assignment? How could they improve their solution? As a bare minimum, I must give them a thorough code review in return.&lt;/p&gt;

&lt;p&gt;When the new employee is hired, I can become a mentor for them. In Futurice, we have the concept of FutuBuddies, and we are the first ones who welcome the new employees into the house on their first day. From there on, we can agree to meet daily or weekly for a catch-up session which is naturally mentoring.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Do We Mentor?
&lt;/h2&gt;

&lt;p&gt;Being a good mentor is a lifelong challenge to which there are no simple answers. We can, however, define techniques every good mentor should possess in their belt. Below are six strategies I've learned to be effective.&lt;/p&gt;

&lt;h3&gt;
  
  
  Don't Shoot Down, Encourage
&lt;/h3&gt;

&lt;p&gt;Let's imagine a situation where a new developer approaches me with the idea of rewriting a codebase with another language. Say, for example, they want to switch from Python to Golang or from Java to Kotlin.&lt;/p&gt;

&lt;p&gt;Chances are they have been only thinking it through themselves without proper feedback, and the cost of unnecessary codebase rewrite might be high. As a mentor, I don't shoot down unprepared initiatives. Instead, I encourage them to think more. What if the developer would organise a workshop on the said language? How could this developer mentor the rest of the team to solve domain problems using the new language more efficiently?&lt;/p&gt;

&lt;p&gt;I have always enjoyed recognising the process bottlenecks in my vicinity and trying to optimise them. Unfortunately, some of my ideas have been shot and even ridiculed in public channels in the far past. People have stated that it couldn't possibly work the way I have imagined. While the others might have had a grain of truth in their words, their approach was not encouraging.&lt;/p&gt;

&lt;p&gt;Luckily, I was born with a strong head and resilience (what in Finland we call &lt;em&gt;sisu&lt;/em&gt;), so the mental damage has been minimal. However, for others, aggressive approaches might even end up terminating promising careers.&lt;/p&gt;

&lt;p&gt;As a mentor, I must always keep in mind &lt;a href="https://en.wikipedia.org/wiki/Reciprocity_%28social_psychology%29"&gt;&lt;em&gt;reciprocity&lt;/em&gt;&lt;/a&gt; from social psychology.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"As a social construct, &lt;strong&gt;reciprocity&lt;/strong&gt; means that in response to friendly actions, people are frequently much nicer and much more cooperative than predicted by the self-interest model; conversely, in response to hostile actions they are frequently much more nasty and even brutal."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Wonder why that co-worker has been ignoring you or acting offensively? It might be what you did to them in the past.&lt;/p&gt;

&lt;h3&gt;
  
  
  Don't Feed Them with a Spoon, Challenge
&lt;/h3&gt;

&lt;p&gt;Sometimes mentees approach with simple questions. Instead of doing their homework and spoon-feeding answers, mentors ask follow-up questions.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Where is the documentation located?&lt;/strong&gt; Where have you looked? Who have you asked?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How can I make my code cleaner?&lt;/strong&gt; Do you see any repeating patterns that could be abstracted to a shared function or library?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Niko is writing crappy code, and it's killing me!&lt;/strong&gt; What are &lt;strong&gt;you&lt;/strong&gt; going to do about it? How can I help to solve this problem without firing Niko?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Don't Reserve, Delegate
&lt;/h3&gt;

&lt;p&gt;As you level up in your career, one of the most rewarding sensations is your increased ability to take on new challenges without feeling like an impostor. The more difficult task at hand, the more confident you are in your ability to tackle it faster and better than others.&lt;/p&gt;

&lt;p&gt;It's not a surprise that mentors do not act like this. Instead of reserving the juiciest tasks to themselves and letting the team do mundane stuff, they delegate. Successful leaders take pride in watching their team complete challenging tasks while staying in the background and occasionally offering help. An approach like this requires rigour, but in the end, you have made your team an unstoppable value delivery machine.&lt;/p&gt;

&lt;p&gt;It's also important to delegate responsibilities. Don't be the only one talking to the client. Let Alex book, prepare and handle the next demo. Let Emma guide the next sprint retro the way they feel would be necessary right now.&lt;/p&gt;

&lt;h3&gt;
  
  
  Don't Hold, Share
&lt;/h3&gt;

&lt;p&gt;Many discussions between mentors and mentees follow the pattern of mentees explaining their situation and mentors asking questions. What's crucial and may often be missed is for mentors to share their experience as well.&lt;/p&gt;

&lt;p&gt;Does your mentee have a difficult time focusing on work due to a personal life crisis? Try to share some of your experiences and how you overcame them – without belittling their experiences. While interviewing a candidate, make the event more relaxed by sharing how anxious you too were in one of your first interviews. Sharing is caring.&lt;/p&gt;

&lt;h3&gt;
  
  
  Set the Rules
&lt;/h3&gt;

&lt;p&gt;We all work under specific rules. Particular rules like reporting our hours for invoicing or delivering work on time are self-explanatory. There are also many invisible rules – conventions – which could help your team further.&lt;/p&gt;

&lt;p&gt;For version control, small atomic commits and conventional commit messages ensure the codebase history is linear and easier to follow. For quality, test-driven development rules us to write only so much code that our tests pass. For faster delivery, continuous integration rules us to push our work into a shared main branch daily.&lt;/p&gt;

&lt;p&gt;As mentors, we set clear rules on how the team works effectively.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do Together
&lt;/h3&gt;

&lt;p&gt;Finally, the shiniest gem of them all is &lt;em&gt;co-creation patterns&lt;/em&gt;. It makes me sad to see how many developers like to work things independently, even in the office. Outdated management views further incentivise solo work. Some managers think that people working on many simultaneous tasks is somehow more effective.&lt;/p&gt;

&lt;p&gt;In truth, we should voraciously swarm to solve problems. We are already doing this early on in the design phase when we co-create with tools like Google Docs, Sketch, and Miro. When it comes to coding, we tend to split tasks as far as possible and let people pick their favourites to work.&lt;/p&gt;

&lt;p&gt;As mentors, we understand that our team is a cohesive unit and not a random bunch of individuals. Lead by example. Always be ready to pair with different people in your team. Experiment and gather feedback regarding what worked and who enjoyed it. Rotate pairs and iterate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building Relationships Through Mentoring
&lt;/h2&gt;

&lt;p&gt;Mentoring is an effective way of getting to know our co-workers. To give meaningful feedback and advice, we should have established a relationship with the other person. Do we know what our mentees are interested in and what goals they have for their career?&lt;/p&gt;

&lt;p&gt;As a mentor, I can inspire these interests and goals myself. Suppose I'm practising clean code and extreme programming principles in a team and continuously showing how I build quality while delivering remarkable results on time. In that case, others will surely follow me with interest and attempt to learn. They can be set off to a great path of software craftsmanship.&lt;/p&gt;

&lt;p&gt;Naturally, as human beings, everything we speak of doesn't have to be professional. After a weekend, I can mind the team member who had to take their cat to a vet on Friday. How is the cat doing now? Continuously showing interest in people's lives helps gain trust and respect, which goes a long way in helping you work better as a team.&lt;/p&gt;

&lt;h2&gt;
  
  
  Traits of a Great Mentor
&lt;/h2&gt;

&lt;p&gt;Finally, here is a non-exhaustive list of traits that matter when developing yourself as a mentor.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Knowledge.&lt;/strong&gt; What experience and skills do you possess. How extensive is your professional background?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authority.&lt;/strong&gt; Who are you? Why should people listen to you? Do other people know what you have achieved?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Confidence.&lt;/strong&gt; How confidently can you express your ideas and solutions?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Charisma.&lt;/strong&gt; What kind of body language do you use? How does your voice sound?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Emotional Intelligence.&lt;/strong&gt; How easy it is for people to trust you. How well can you empathise with others? How deeply can you collaborate?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Team reputation.&lt;/strong&gt; How successful your team and its members are in the eyes of the organisation and client?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Thought leadership.&lt;/strong&gt; Are you a recognised blogger or speaker? Are people lining up to work with you?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep calm and carry on &lt;strong&gt;mentoring&lt;/strong&gt;!&lt;/p&gt;




&lt;p&gt;This is the second post about observations I gained from &lt;a href="https://principal.dev/"&gt;&lt;strong&gt;The Principal Developer&lt;/strong&gt;&lt;/a&gt; workshop. Read also &lt;a href="https://nikoheikkila.fi/blog/reducing-the-lead-times-with-littles-law/"&gt;my earlier post&lt;/a&gt; on reducing the work-in-progress and increasing throughput following Little's Law.&lt;/p&gt;

&lt;p&gt;Photo by &lt;strong&gt;John Schnobrich&lt;/strong&gt; on &lt;a href="https://unsplash.com/photos/2FPjlAyMQTA"&gt;Unsplash&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>career</category>
      <category>psychology</category>
      <category>leadership</category>
      <category>management</category>
    </item>
    <item>
      <title>Reducing the Lead Times with Little's Law</title>
      <dc:creator>Niko Heikkilä</dc:creator>
      <pubDate>Tue, 30 Mar 2021 18:18:10 +0000</pubDate>
      <link>https://dev.to/futurice/reducing-the-lead-times-with-little-s-law-1ok1</link>
      <guid>https://dev.to/futurice/reducing-the-lead-times-with-little-s-law-1ok1</guid>
      <description>&lt;p&gt;There's a fascinating law of mathematical theory applicable to agile software development, which helps you deliver features faster without sacrificing quality. It's called &lt;a href="https://toggl.com/track/littles-law/"&gt;&lt;strong&gt;Little's Law&lt;/strong&gt;&lt;/a&gt;, and it dictates that the average lead time of delivery is the team's work in progress amount divided by its throughput.&lt;/p&gt;

&lt;p&gt;First, let's define the terms.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Work in progress&lt;/strong&gt; (WIP) is the number of backlog items the team is currently working on. These items are not yielding value for the customer. WIP can be determined by counting those cards in your project board that are not in the &lt;em&gt;Backlog&lt;/em&gt; column and not in the &lt;em&gt;Done&lt;/em&gt; column but somewhere in between.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Throughput&lt;/strong&gt; is the measured amount of backlog items a team can complete in a fixed period, which is usually a day.&lt;/p&gt;

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

&lt;p&gt;The team has 20 tasks in progress, and its average throughput is four cards/day. Following the law, this gives an average lead time of &lt;strong&gt;20/4&lt;/strong&gt; = &lt;strong&gt;5&lt;/strong&gt; days.&lt;/p&gt;

&lt;p&gt;To foster an effective software development process, we should target our efforts towards shortening the lead time. Following the laws of mathematics, we can achieve this by limiting the WIP and increasing the throughput.&lt;/p&gt;

&lt;p&gt;Often we fool ourselves to think that high WIP and high throughput are synonymous with performant processes, but this leads to stagnation, low motivation, low innovation, and burnouts.&lt;/p&gt;

&lt;p&gt;In this post, I attempt to share approaches to doing it right, but your mileage may vary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reducing the Work in Progress
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;A golden rule of limiting the WIP is to &lt;strong&gt;stop starting and start finishing&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Total WIP should be smaller than the number of people in the team. If possible, strive for a WIP limit of &lt;strong&gt;one&lt;/strong&gt;, but mind that it's not always a feasible choice.&lt;/p&gt;

&lt;p&gt;When working under a strict WIP limit, you should leverage the daily stand-up with your team for prioritizing and selecting the tasks for today. If the WIP limit is reached, nobody is allowed to start new work. Instead, everybody should help finish the ongoing work – be it developing, reviewing, testing, or deploying a new feature.&lt;/p&gt;

&lt;p&gt;Teams wanting to reduce WIP should focus as many people as possible on as few tasks as possible. Some people think this only applies to Kanban, but Scrum too has a specific technique for this: &lt;a href="https://www.scruminc.com/swarming-instantly-boost-scrum-team-productivity/"&gt;&lt;em&gt;swarming&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Pair/mob programming is also a natural and effective enemy towards high WIP. Instead of distributing one task for each developer, make them solve the problem together. Pairing and mobbing are not only for programming, as you can also practice design, testing, monitoring, leadership, and many other tasks together.&lt;/p&gt;

&lt;p&gt;Essentially, when people pile on a single task, they can design, develop, test, and deliver it more cost-efficiently. Mobbing has its limits, though, and we should find balance through careful studying. Too many people working on a single task invokes &lt;a href="https://www.investopedia.com/terms/l/lawofdiminishingmarginalreturn.asp"&gt;the law of diminishing returns&lt;/a&gt; and eventually hurts the performance.&lt;/p&gt;

&lt;p&gt;Additionally, to make the mobbing work, all teams should be cross-functional, consisting of designers, developers, testers, and deployers with a well-balanced skill set. These teams deliver &lt;a href="https://en.wikipedia.org/wiki/Vertical_slice"&gt;&lt;em&gt;vertical slices&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Note that the latter two roles don't often require separate people because developers can test and deploy the changes themselves. In case you have an independent QA team, make sure to involve them early on in the process, tear down the walls, and enable deep collaboration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Increasing the Throughput
&lt;/h2&gt;

&lt;p&gt;There are three actions a company can do to increase its teams throughput.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Hire more people, but not too much (consider &lt;a href="https://jasoncrawford.org/two-pizza-teams"&gt;a two-pizza team&lt;/a&gt; 🍕🍕 the maximum size – leaving the exact number for you to decide).&lt;/li&gt;
&lt;li&gt;Make the team more proficient by fostering &lt;a href="https://en.wikipedia.org/wiki/Extreme_programming#Principles"&gt;Extreme Programming principles&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Eliminate patterns of waste (see below).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I consider XP principles and eliminating waste very effective actions here. Below are the most common forms of waste you may encounter in your development process.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Partially Done Work
&lt;/h3&gt;

&lt;p&gt;We cancel or postpone tasks, our work is stuck in the testing queue, and essential features are waiting for deployment. Swarming and a low WIP limit ensures the team is focused on a fixed number of tasks at any given time. Remember, partially done work does not yield value.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Overproduction
&lt;/h3&gt;

&lt;p&gt;We are often tempted to design non-trivial, abstract, and flexible code for the speculative future, which might never happen. We may add complex data analysis and charts functionality to the project instead of adding an &lt;strong&gt;Export as CSV&lt;/strong&gt; button. Instead, think about how you can solve problems with less or no code.&lt;/p&gt;

&lt;p&gt;We might drive ourselves into an endless loop during refactoring code where we polish secondary or unimportant code. Such code might not even change that often nor slow us down, so why bother with it?&lt;/p&gt;

&lt;p&gt;Alternatively, we apply the best development principles while developing throw-away prototypes. Refactor only so much to keep the code maintainable and easy to understand until the next time it's changed.&lt;/p&gt;

&lt;p&gt;We might deliver features with low or negative user impact. We should carefully observe and respond to UX metrics and customer feedback.&lt;/p&gt;

&lt;p&gt;We adopt trendy technologies and tools, whereas the tools we already know how to use would suffice (see &lt;a href="https://en.wikipedia.org/wiki/Marchitecture"&gt;&lt;em&gt;Marchitecture&lt;/em&gt;&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;We chase 100% test coverage for our unit, integration, and acceptance test suites. Still, we ship design defects that are difficult to detect and recover from in case of disasters. Customer finding out a bug in production before your team does is a bad, bad thing.&lt;/p&gt;

&lt;p&gt;Our product owners maintain huge backlogs with more user stories than needed for the next few sprints. &lt;a href="https://www.martinfowler.com/bliki/Yagni.html"&gt;&lt;em&gt;YAGNI&lt;/em&gt;&lt;/a&gt; rule is the single best defence shielding us from this.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Waiting, Delays, and Handoffs
&lt;/h3&gt;

&lt;p&gt;We are maintaining a slow and unstable regression test suite relying on external integrations. Running it keeps us waiting for results due to constant failures. Additionally, the build server is shared with other teams and maintained by a fantasy creature referred to as the &lt;em&gt;DevOps Engineer&lt;/em&gt;. Why not use on-demand testing environments, or even better: run all the tests locally?&lt;/p&gt;

&lt;p&gt;For the better part of the lead time, the code is waiting for someone to review it. Some features require review from a senior developer who is at the moment drinking mojitos in Bali. Align the team's skills so knowledge gaps can't happen.&lt;/p&gt;

&lt;p&gt;Teammates are involved in multiple projects, and the same human resources are being shared across squads. Build long-lasting teams with domain expertise as a core objective.&lt;/p&gt;

&lt;p&gt;Unexpected, critical, and non-trivial production incidents might pop up in the middle of other work delaying the feature work further. Good observability and XP principles protect us from this.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Rework
&lt;/h3&gt;

&lt;p&gt;Pull requests are bounced back and forth between the author and reviewer in asynchronous code reviews. Use &lt;a href="https://trunkbaseddevelopment.com/"&gt;trunk-based development&lt;/a&gt;, &lt;a href="https://martinfowler.com/articles/on-pair-programming.html"&gt;pair programming&lt;/a&gt;, and a &lt;a href="https://martinfowler.com/articles/continuousIntegration.html"&gt;continuous integration&lt;/a&gt; mindset to avoid this.&lt;/p&gt;

&lt;p&gt;We need to re-implement solutions due to misunderstood requirements or fix the same bugs twice. If the product owner can't define the requirements, go and talk to the customer yourself. Use &lt;a href="https://en.wikipedia.org/wiki/Behavior-driven_development"&gt;behaviour-driven development&lt;/a&gt; to make sure work is only considered done when acceptance criteria are met.&lt;/p&gt;

&lt;p&gt;Due to poor communication, we need to answer the same questions, or the lack of automation forces us to run mundane routines repeatedly. We should capture the necessary knowledge in software documentation and &lt;a href="https://adr.github.io/"&gt;architectural decision records&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Non-value added activities
&lt;/h3&gt;

&lt;p&gt;Long-lived feature branches force us to rebase continuously and deal with resulting merge conflicts. Stop branching and start integrating.&lt;/p&gt;

&lt;p&gt;At the end of each sprint, we have a retrospective without action points and assigned responsibilities. Shake up the process and start setting action points.&lt;/p&gt;

&lt;p&gt;Our calendars are filled with never-ending meetings and attempts to reach consensus without making progress (impasse). Apply Basecamp's &lt;a href="https://basecamp.com/guides/how-we-communicate"&gt;internal communication strategies&lt;/a&gt; to heal this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Little's law is one of the most powerful drivers in serious software development efforts because of its simplicity. You don't have to maintain complex models or calculations to observe your delivery performance – a fractional operation does it for you.&lt;/p&gt;

&lt;p&gt;I gained the majority of these observations in reducing the WIP and eliminating waste during &lt;a href="https://principal.dev"&gt;&lt;strong&gt;The Principal Developer&lt;/strong&gt;&lt;/a&gt; workshop. Workshops like these provide invaluable tools altering the way you think about processes, teams, and cultures. I argue they offer a more significant return-on-investment in your career than simply learning frameworks and tools like React or Docker.&lt;/p&gt;




&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@epicantus?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Daria Nepriakhina&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/agile?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>agile</category>
      <category>productivity</category>
      <category>leadership</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Blocking Time for Tasks with Toggl</title>
      <dc:creator>Niko Heikkilä</dc:creator>
      <pubDate>Mon, 22 Mar 2021 07:05:39 +0000</pubDate>
      <link>https://dev.to/futurice/blocking-time-for-tasks-with-toggl-2jic</link>
      <guid>https://dev.to/futurice/blocking-time-for-tasks-with-toggl-2jic</guid>
      <description>&lt;p&gt;For people doing mainly project work, planning daily tasks is a constant struggle, which can cause severe issues with productivity and mental well-being.&lt;/p&gt;

&lt;p&gt;As a software developer with multiple projects on my daily schedule, I used to waddle in a sea of chaos. Attempting to swim and survive throughout my days impacted my work in many ways. The loss of context made it difficult to focus on tasks, which caused my productivity to halt and gave a diminishing sense of accomplishment in return.&lt;/p&gt;

&lt;p&gt;Furthermore, when I needed to recall what I had been doing on a given day, I had to resort to checking my browsing history. As you might guess, it had little relevance to what I had worked. Daily standup meetings mainly were a burden where I was supposed to figure out what I did yesterday and what shall I do today. Similarly, the reported hours contained some time to this project and some time to that.&lt;/p&gt;

&lt;p&gt;The technique of surviving with simple to-do lists — where &lt;strong&gt;Todoist&lt;/strong&gt; is still the king — I had learned previously was suitable for single-project environments. There I could split my days between writing and reviewing code in one domain. It didn't scale at all for multiple sequential tasks. Fortunately, later on, I discovered the magnificent technique called &lt;a href="https://todoist.com/productivity-methods/time-blocking"&gt;&lt;em&gt;time blocking&lt;/em&gt;&lt;/a&gt;, where every minute of the day is given a purpose.&lt;/p&gt;

&lt;p&gt;Let me tell you how.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basics of Time Blocking
&lt;/h2&gt;

&lt;p&gt;Time blocking means saving time for important — not necessarily urgent — tasks in your calendar. With your chosen tool, start by dividing a day into slices and decide &lt;strong&gt;one and only one theme&lt;/strong&gt; you're working on during a particular time block.&lt;/p&gt;

&lt;p&gt;Don't worry. With time blocking, you get to keep your regular breaks and days off. This post is not one of those foolish blurbs hailing 60+ hour work weeks and waking up 4:30 every day to solve productivity issues.&lt;/p&gt;

&lt;p&gt;After dividing your days into slices, your day might look a bit like in the picture below. Note that the themes are only examples, and I usually write those down precisely to help me later recall what I worked on.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LdGm7snA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l8xm079f7lh5jeedg4b6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LdGm7snA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l8xm079f7lh5jeedg4b6.png" alt="Time blocking example sketch"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How Do I Split the Day?
&lt;/h2&gt;

&lt;p&gt;My typical day is as follows. Due to the dynamic nature of my profession, some variations may occur, but mostly it's easy to stick with the plan.&lt;/p&gt;

&lt;h3&gt;
  
  
  From Morning to Noon
&lt;/h3&gt;

&lt;p&gt;I start working between 8–9 in the morning and sketch my time blocks for the day. Next, I begin responding to urgent stuff like emails and messages before clearing out the path for focusing on tasks. Then I grab a few planned tasks and complete them at my own pace.&lt;/p&gt;

&lt;p&gt;After the first round of intensive focus, I attend a daily standup with my team. Here I leverage the time blocks from yesterday and today while explaining my status and possible blockers. If any meetings have been invited, they are usually held around mid-day.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lunch Break / Time Off
&lt;/h3&gt;

&lt;p&gt;With the first half of the day completed, I allow myself to take a decent amount of time off eating, drinking and relaxing. Together with the daily shutdown, it's one of the most important events of the day, and you shouldn't skip it.&lt;/p&gt;

&lt;h3&gt;
  
  
  From Noon to Afternoon
&lt;/h3&gt;

&lt;p&gt;When I return to focus work, I then go over the pending code reviews or help team members debugging issues. This kind of work fits my afternoon mental state as most of the urgent stuff has already been done and now there's time to shine some creative light.&lt;/p&gt;

&lt;p&gt;To call it a day, I begin slowing the pace down around 16–17 and make note of all the tasks I've achieved. Shutting down at regular times is essential for the mind to function correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Toggl Track to the Rescue
&lt;/h2&gt;

&lt;p&gt;I first tried using traditional paper and the &lt;strong&gt;Remarkable&lt;/strong&gt; tablet for time blocking, but having my schedules tied to one device quickly drove me to use &lt;a href="https://track.toggl.com"&gt;&lt;strong&gt;Toggl Track&lt;/strong&gt;&lt;/a&gt;. It comes with excellent desktop, mobile, and web apps allowing me to access and edit my schedule from virtually anywhere. I don't use Toggl's flagship timer feature that much. The very intention of time blocking is to plan and control my time spent instead of spontaneous tracking.&lt;/p&gt;

&lt;p&gt;Toggl web app has a nifty calendar view which is ideal for drawing blocks of time with a mouse and marking them to specific projects. You can also extend it with your calendar. I've hooked my Google work calendar into it, which allows me to duplicate each calendar event as a time block. Fun fact: when you're involved in a project with all-day meetings the time-blocking is automatically there, but it will hurt you in other ways.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Command-Line? Why Not?
&lt;/h2&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--i3JOwpme--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/nikoheikkila"&gt;
        nikoheikkila
      &lt;/a&gt; / &lt;a href="https://github.com/nikoheikkila/hours"&gt;
        hours
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A command-line companion for the Toggl Track software. Check the README for supported features.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
⏰ Hours&lt;/h1&gt;
&lt;p&gt;Working as a consultant requires you to mark your billed project hours periodically for invoicing. This is boring but luckily &lt;a href="https://toggl.com/" rel="nofollow"&gt;&lt;strong&gt;Toggl&lt;/strong&gt;&lt;/a&gt; is there to help. Unfortunately, not everyone is using it directly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hours&lt;/strong&gt; is a command-line tool built with &lt;strong&gt;Go&lt;/strong&gt; for integrating to Toggl Track. Currently, it supports the following features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;listing saved time entries in various formats&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Upcoming and planned features are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;creating new time entries&lt;/li&gt;
&lt;li&gt;deleting saved time entries&lt;/li&gt;
&lt;li&gt;starting and stopping the timer on certain tasks&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
Documentation&lt;/h2&gt;
&lt;p&gt;Documentation is divided into the following sections.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://raw.githubusercontent.com/nikoheikkila/hours/main/docs/01-installation.md"&gt;Installation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://raw.githubusercontent.com/nikoheikkila/hours/main/docs/02-listing.md"&gt;Listing time entries&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/nikoheikkila/hours"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;However, one thing I was missing from Toggl was a performant terminal app. So, as a hobby project and because my current employer sponsors the personal time spent in open-source, I started writing one. Enter &lt;a href="https://github.com/nikoheikkila/hours"&gt;&lt;strong&gt;Hours&lt;/strong&gt;&lt;/a&gt;. It's a lightweight, single-binary, open-source terminal companion for Toggl written in &lt;strong&gt;Go&lt;/strong&gt;. At the moment, Hours ships the following features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;listing the saved time entries in various formats using the public Toggl API&lt;/li&gt;
&lt;li&gt;styling the plain text output with &lt;code&gt;termenv&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;converting the output to Markdown, JSON, and CSV allowing you to post-process data with other scripts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fortunately, the Toggl API is convenient to work with and I've been delighted how easy it is to marshal Go structures to JSON and vice versa. Thus, more features are definitely on the roadmap. I'm thinking of implementing features such as starting and stopping timers without forgetting the basic time entry CRUD operations (create, read, update, and delete). Since &lt;strong&gt;Hours&lt;/strong&gt; is still in the very early stages of development, you can help me improve it!&lt;/p&gt;

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

&lt;p&gt;Blocking time ensures I can complete essential tasks while still reacting to urgent tasks. Having a clear daily plan helps me to recall individual days back to mind. I can also enjoy the rewarding sensation of accomplishing tasks. Most importantly, it helps me to stay away from unnecessary distractions. By the way, I don't have any desktop or web notifications enabled while working, so deal with it. Naturally, time blocking can't eliminate all the stress that a busy world with many projects brings forth, but it guarantees continuous calmness throughout the day.&lt;/p&gt;

&lt;p&gt;Throw me with a message if you need help organizing your day with time blocking.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Photo by &lt;strong&gt;Aron Visuals&lt;/strong&gt; on Unsplash&lt;/em&gt;&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>tooling</category>
      <category>go</category>
      <category>showdev</category>
    </item>
    <item>
      <title>My 21st Century Note-Taking Workflow</title>
      <dc:creator>Niko Heikkilä</dc:creator>
      <pubDate>Mon, 08 Feb 2021 14:16:36 +0000</pubDate>
      <link>https://dev.to/nikoheikkila/my-21st-century-note-taking-workflow-46np</link>
      <guid>https://dev.to/nikoheikkila/my-21st-century-note-taking-workflow-46np</guid>
      <description>&lt;p&gt;Taking notes is a serious business. While I'm not your typical person journaling everything they see or hear, I write a lot whether it be articles, documentation or code. All of those require an optimised workflow for capturing the ideas, refining them, and finally shaping them to actual distributable fruits of thought.&lt;/p&gt;

&lt;p&gt;In the past, I've experimented with various note-taking techniques. I began scribbling into old good text files on a hard disk with &lt;strong&gt;Notepad++&lt;/strong&gt; -- those were the days. It worked solidly, and I only resorted to switching my text editor between &lt;em&gt;Vim&lt;/em&gt;, &lt;em&gt;Sublime Text&lt;/em&gt;, &lt;em&gt;Atom&lt;/em&gt;, and &lt;em&gt;VS Code&lt;/em&gt;. However, soon, I came across the need to view my notes on the phone, and the bad experience of synchronising simple documents via Dropbox and Google Drive drove me to my current workflow, fortunately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Too long, didn't read?&lt;/strong&gt; In brief, I jot down thoughts onto a paper tablet, transfer them to Notion, proofread them in Grammarly, and save the resulting products as finely organised as text files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Capturing the Ideas
&lt;/h2&gt;

&lt;p&gt;Whenever ideas worth keeping start to pour in, I grab my &lt;a href="https://remarkable.com/"&gt;&lt;strong&gt;Remarkable 2&lt;/strong&gt;&lt;/a&gt; paper tablet and retreat into a peaceful corner to capture them.&lt;/p&gt;

&lt;p&gt;The handwriting experience with Remarkable is precisely like with paper, if not even better. The pen reacts to different angles and pressures like a real one, and you never have to worry about the dried ink or sharpening. I can use a collection of different pens (fine-liner, ballpoint, mechanical pencil, and calligraphy pen mostly) and the eraser without changing the physical tool in my hand. On the hindsight, I should have bought the more expensive Remarkable pen that has the eraser equipped on the opposite end. Opening the sidebar menu and switching between the eraser and the pen is not too cumbersome, though.&lt;/p&gt;

&lt;p&gt;Within Remarkable's filesystem, I've created folders for all the significant projects, and mostly I'm using a standard notebook with horizontal lines for writing down thoughts. At this stage, everything is an unrefined, unfiltered downpour of my mental stream.&lt;/p&gt;

&lt;p&gt;Remarkable is also an ideal device for reading and editing PDFs. I don't meddle much in the world of academic research. Thus I haven't used that particular feature much. Nevertheless, it shines in signing documents which for certain people still seems to be the way despite living in an era of digital signatures.&lt;/p&gt;

&lt;p&gt;One of Remarkable's rare downsides is its export feature, which currently only allows me to email the text to myself. These emails also often end up in my spam folder if I'm using my work email, but let's blame Google for that. It would be useful to launch the desktop app, convert the handwritten text to digital, and copy it to my clipboard. Exporting drawings in PDF or PNG formats, however, works well enough when I need it.&lt;/p&gt;

&lt;p&gt;After exporting the rough notes, it's time to start refining them digitally.&lt;/p&gt;

&lt;h2&gt;
  
  
  Digital Refinement
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://notion.so"&gt;&lt;strong&gt;Notion&lt;/strong&gt;&lt;/a&gt; is an excellent tool for carrying around a personal wiki. Be warned that it requires some level of organising your workspace, but nothing that I couldn't handle.&lt;/p&gt;

&lt;p&gt;Inside my Notion workspace, I have multiple projects as individual pages. Within those pages, I have small &lt;em&gt;databases&lt;/em&gt; where each row represents a note. This makes it easy for me to find content, although usually, I resort to the &lt;em&gt;Quick Search&lt;/em&gt; feature (&lt;code&gt;Cmd/Ctrl + P&lt;/code&gt;) to get around.&lt;/p&gt;

&lt;p&gt;After pasting the rough notes from email to Notion, the process is as simple as converting bullet points to entire paragraphs, adding links, improving formatting. With this crucial step, I ensure the raw blob becomes closer to a readable document.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shaping the End Result
&lt;/h2&gt;

&lt;p&gt;However, Notion is not the be all end all of my drafting process. From there, I copy and paste to &lt;a href="https://grammarly.com/"&gt;&lt;strong&gt;Grammarly&lt;/strong&gt;&lt;/a&gt; for proofreading.&lt;/p&gt;

&lt;p&gt;I have a paid Grammarly subscription which not only corrects my mistakes but helpfully teaches me to write better sentences. It's a golden tool for everyone who is not a native English speaker. Even if you are, it gives you priceless tips for adjusting your writing and tone to different audiences. For critical documents, I should consult a human proofreader, but most of the time, simple automation works best.&lt;/p&gt;

&lt;p&gt;Besides source code, the vast majority of my writing is either in documentation or articles. What is common with them is that everything is stored as Markdown files in version control. At this stage, I rarely edit the files anymore, but there are useful Markdown extensions for VS Code if the need arises.&lt;/p&gt;

&lt;p&gt;I strongly advocate storing all meaningful descriptions, decisions, and instructions next to your source code for developers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nik Begley&lt;/strong&gt; wrote a fantastic post titled &lt;a href="https://dev.to/niklasbegley/confluence-is-where-documentation-goes-to-die-3ank"&gt;&lt;em&gt;Confluence Is Where Documentation Goes To Die&lt;/em&gt;&lt;/a&gt;. Storing your written content in an external &lt;strong&gt;WYSIWYG&lt;/strong&gt; (&lt;em&gt;what you see is what you get&lt;/em&gt;) system outside everyday workflows is a guaranteed cause of the content's premature death. That's why I don't store the final editions of my text in Notion, either. Instead, my blog articles are in my blog's repository and documentation next to the source code it's describing. This is the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  One Tool, One Responsibility
&lt;/h2&gt;

&lt;p&gt;I'm fond of workflows where each tool has a single concrete responsibility. When it comes to my note-taking pipeline, &lt;strong&gt;Remarkable&lt;/strong&gt;, &lt;strong&gt;Notion&lt;/strong&gt;, &lt;strong&gt;Grammarly&lt;/strong&gt;, and &lt;strong&gt;VS Code&lt;/strong&gt; all have a distinct role where they shine and won't overlap each other.&lt;/p&gt;

&lt;p&gt;Previously I tried coping with fewer tools which effectively bloated my workflow and decreased my productivity. For instance, &lt;a href="https://www.vscodecandothat.com/"&gt;VS Code can perform a great many things&lt;/a&gt;, but taking notes, drafting them to real text, and proofreading in the same application would strip me of the possibility to look at my work from different perspectives in different applications, which is more important than speed. Hats off to you who can coordinate your entire work inside one &lt;strong&gt;Emacs&lt;/strong&gt; buffer, though.&lt;/p&gt;

&lt;p&gt;Your workflow may vary. Now go and write great things.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>writing</category>
      <category>tooling</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Effortless End-To-End Testing with Microsoft Playwright</title>
      <dc:creator>Niko Heikkilä</dc:creator>
      <pubDate>Sun, 10 Jan 2021 11:57:19 +0000</pubDate>
      <link>https://dev.to/nikoheikkila/effortless-end-to-end-testing-with-microsoft-playwright-47j8</link>
      <guid>https://dev.to/nikoheikkila/effortless-end-to-end-testing-with-microsoft-playwright-47j8</guid>
      <description>&lt;p&gt;In my job, I stumble upon projects where there are &lt;em&gt;zero&lt;/em&gt; tests, or a project is equipped with a single generated test always failing. For some development teams, writing and maintaining a comprehensive automated test suite is a lesser priority. These teams' end product is usually an application that satisfies the bare necessities asked by the client but is plagued by code rot, technical debt, and instability.&lt;/p&gt;

&lt;p&gt;During the maintenance phase, new layers of code are added on top of the existing cruft, making things worse. Fear of breaking things prevents refactoring, and hence the code quality keeps dropping.&lt;/p&gt;

&lt;p&gt;Software maintenance and lifecycle management are hard. As maintainers, we are often tempted to heroically rescue the project by adding all the missing tests or rewriting the whole project from scratch. No budget will ever be given for such a crazy thing. Fortunately, we can follow certain practices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;adding tests whenever writing new functions, methods, or classes&lt;/li&gt;
&lt;li&gt;adding tests before refactoring a unit of code&lt;/li&gt;
&lt;li&gt;studying the business logic and writing end-to-end tests for it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this post, I explain the latter -- &lt;strong&gt;end-to-end tests&lt;/strong&gt; -- which I've found to be the most effective way of refreshing a stale project. What are these tests, you may ask?&lt;/p&gt;

&lt;h2&gt;
  
  
  Anatomy of an End-to-End Test
&lt;/h2&gt;

&lt;p&gt;End-to-end tests (abbreviated as E2E from now on) instead of unit and integration tests cover user-facing flows and business requirements defined for the application since it's planning phase started. These include, for example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;registering a new user for a service&lt;/li&gt;
&lt;li&gt;signing in and out of a service as an existing user&lt;/li&gt;
&lt;li&gt;adding products to the basket and checking out the purchase in an e-commerce website&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Experienced developers might now stop and think of &lt;em&gt;behaviour-driven development&lt;/em&gt; (BDD) which goes hand-in-hand with E2E. Behaviour flows are often decided when planning a new feature.&lt;/p&gt;

&lt;p&gt;Say we have a calculator application, for example. We want to implement the adding feature and start drafting a specification for it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gherkin"&gt;&lt;code&gt;&lt;span class="kd"&gt;Feature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; Calculator

  &lt;span class="kn"&gt;Scenario&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; “+” should add to current total.
    &lt;span class="nf"&gt;Given &lt;/span&gt;the current total is “5”.
    &lt;span class="nf"&gt;When &lt;/span&gt;I enter “7”.
    &lt;span class="nf"&gt;Then &lt;/span&gt;the current total should be “12”.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The syntax in the snippet above is called &lt;em&gt;gherkin&lt;/em&gt;, and it can be used to convert human-readable specifications to machine-readable tests! Thus, E2E tests can and should be written before the business logic.&lt;/p&gt;

&lt;p&gt;Here, the above specification would result in the following test code. The below example is partly written in pseudo-code for the sake of simplicity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Calculator&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;+ should add to current total&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;initialTotal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
        &lt;span class="nx"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;

        &lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.plus&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.run&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="nx"&gt;newTotal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.total&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt;
        &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newTotal&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialTotal&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;number&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;E2E tests are easy to write before the business logic, but they are also easy to write months or years afterwards. This makes them a powerful asset in a project.&lt;/p&gt;

&lt;p&gt;But who should write them? &lt;strong&gt;Developers!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem of Divergent Tests in Software Development Teams
&lt;/h2&gt;

&lt;p&gt;In some organisations, product teams consist of developers and QA specialists often separated by mental or physical walls. We may use &lt;a href="https://en.wikipedia.org/wiki/Conway%27s_law"&gt;Conway's Law&lt;/a&gt; to dictate that tests produced by two different groups who don't communicate with each other are radically different.&lt;/p&gt;

&lt;p&gt;Developers stick with tests close to their daily work implementing low-level unit tests with lots of mocks, fakes, and stubs. Their tests run fast, use modern features of their chosen programming language, and mostly deliver stable results. The end product is a technically ambitious test suite with low business value and confidence. Most of the functionality might be broken, but at least my fancy new method always returns the correct list of dummy user objects using a mocked database connection. What more do I need?&lt;/p&gt;

&lt;p&gt;Meanwhile, when not testing features by hand, QA struggles to write and maintain an exhaustive test set, ultimately proving the application works as the client wants. It's a pity their test suite requires laborious setup, runs slowly, and crashes half of the runs.&lt;/p&gt;

&lt;p&gt;Let's put this in the form of a picture.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--X-53E_6d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/btt6gi4scce9o9kmroo1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--X-53E_6d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/btt6gi4scce9o9kmroo1.png" alt="Typical tests created by QA and developers."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The root cause of flaky E2E tests is naturally communication, but sometimes choosing the right tools may also help. The QA boomers (pardon the expression) who have been working in the software business for decades often write E2E tests with &lt;strong&gt;Robot Framework&lt;/strong&gt; and &lt;strong&gt;Selenium&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;As a developer coming from Finland, I can say &lt;strong&gt;Nokia&lt;/strong&gt; indeed has had a significant impact on our professional lives and education. Still, I don't shy away from saying that tests written in Robot Framework are a devastating travesty and maintenance nightmare. Since Cypress began gaining popularity, it has been our time to move on.&lt;/p&gt;

&lt;p&gt;Typically, Robot tests are written in a quirky tabular keyword syntax advertised as accessible  to even non-developers -- which it certainly is not, I dare to say. Robot tests almost always become a jumbled mess of keywords and external Python libraries without rigorous engineering practices. In a worst-case scenario, as the test suite complexity grows over time, people start neglecting it and what is left is a pit of obsolete tests that worked five years ago, but the application has lived on and changed a lot since then.&lt;/p&gt;

&lt;p&gt;The solution is two-fold:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;End the Cold War, and bring QA and developers together&lt;/li&gt;
&lt;li&gt;Throw away Robot framework&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The former is an organizational and cultural topic outside the scope of this post. The latter, however, can easily be fixed with modern tools. One prominent newcomer to the scene is &lt;a href="https://playwright.dev/"&gt;&lt;strong&gt;Playwright&lt;/strong&gt;&lt;/a&gt; by &lt;strong&gt;Microsoft&lt;/strong&gt;, which I've enjoyed a lot lately.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Playwright Reduces the Burden of Automated Testing
&lt;/h2&gt;

&lt;p&gt;For those who have used &lt;strong&gt;Puppeteer&lt;/strong&gt;, the logic and API behind Playwright should be familiar. You control three different browser engines. As of writing this they are &lt;strong&gt;Chromium&lt;/strong&gt;, &lt;strong&gt;Firefox&lt;/strong&gt;, and &lt;strong&gt;Webkit&lt;/strong&gt;. Browsers can handle multiple windows and tabs through contexts. There is also support for different mobile devices like iPhones and Pixel phones through device emulation.&lt;/p&gt;

&lt;p&gt;There are a couple of aspects that make Playwright especially pleasant to work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Speed and Stability Matter
&lt;/h3&gt;

&lt;p&gt;Page actions can be run with &lt;code&gt;async/await&lt;/code&gt; logic one by one or concurrently when wrapped inside &lt;code&gt;Promise.all()&lt;/code&gt; or &lt;code&gt;Promise.allSettled()&lt;/code&gt; calls. Furthermore, page operations automatically wait for the targeted element to become &lt;em&gt;actionable.&lt;/em&gt; So, for example, a button is not clicked until it's visible on-page and enabled. Never resort to &lt;code&gt;Sleep  5&lt;/code&gt;  or other shenanigans again.&lt;/p&gt;

&lt;p&gt;Playwright scripts run incredibly fast with the default configuration, so you may want to add a little delay for the human eye to catch up. Most of the time you should be running your tests headless (no visible browser) and without delay, though.&lt;/p&gt;

&lt;h3&gt;
  
  
  Support for Modern Languages
&lt;/h3&gt;

&lt;p&gt;Developers can write tests in &lt;strong&gt;JavaScript&lt;/strong&gt; or &lt;strong&gt;TypeScript&lt;/strong&gt; using all the modern features as long as a recent Node version is used. I've been running my tests with the latest LTS version (v14) using all the ES2020 goodness through TypeScript configuration.&lt;/p&gt;

&lt;p&gt;Using &lt;strong&gt;VS Code&lt;/strong&gt;, I'm also able to attach the debugger right in the middle of a test to investigate where that one element disappeared.&lt;/p&gt;

&lt;p&gt;If the languages mentioned above are not your cup of tea, you may also use other &lt;a href="https://playwright.dev/docs/languages"&gt;supported languages&lt;/a&gt; which currently include &lt;strong&gt;Python&lt;/strong&gt;, &lt;strong&gt;C#&lt;/strong&gt;, and &lt;strong&gt;Golang&lt;/strong&gt;. These are currently considered to be in preview and not ready for production use, but feel free to tinker away.&lt;/p&gt;

&lt;p&gt;Upside with multiple language support is that QA specialists and other people with less programming background can start writing tests with simple languages like Python. In contrast, more experienced people appreciate the robust type systems found in TypeScript and Golang. Naturally, the most important thing is you don't have to use Robot Framework keywords anymore.&lt;/p&gt;

&lt;h3&gt;
  
  
  Extendability
&lt;/h3&gt;

&lt;p&gt;Because Playwright does not ship with an assertion library or a test runner, you may use any libraries you're familiar with be it &lt;code&gt;assert&lt;/code&gt;, &lt;code&gt;chai&lt;/code&gt;, or &lt;code&gt;expect&lt;/code&gt;. As for running the tests, Jest is recommended because it takes a lot of setup code out of your hands so you can focus on writing the actual test cases.&lt;/p&gt;

&lt;p&gt;You can also use any third-party NPM modules in your tests. For example, &lt;a href="https://github.com/marak/Faker.js/"&gt;&lt;code&gt;faker.js&lt;/code&gt;&lt;/a&gt; library proves useful when testing HTML form validation with random data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Easy Maintenance
&lt;/h3&gt;

&lt;p&gt;More than often, E2E tests contain a lot of repeated actions which can be abstracted into common methods for increased maintainability. Playwright did not invent the &lt;strong&gt;Page Object Model&lt;/strong&gt; (POM), but using them proves useful here as well. In brief, POMs are regular classes which are instantiated with a &lt;code&gt;Page&lt;/code&gt; object responsible for page actions. Remember to declare your methods asynchronous.&lt;/p&gt;

&lt;p&gt;Using a POM, the following operation of signing a user in...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-test-id=username]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-test-id=password]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-test-id=login]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...becomes a more readable version&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;LoginPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;fillCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-test-id=username]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-test-id=password]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-test-id=login]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that above I've removed the &lt;code&gt;Promise.all()&lt;/code&gt; wrap for the sake of simplicity. After the abstraction, this model can be used for any further tests requiring the user to be authenticated.&lt;/p&gt;

&lt;p&gt;You should write a model for each specific page in your application and import them to your test suite when needed. If necessary, you can create a new page object with Playwright if sharing one is not an option (it usually is). You can test single-page applications without model classes, but they offer a significant productivity boost for testing more complex routes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Generate Code by Recording Browser Actions
&lt;/h3&gt;

&lt;p&gt;Suppose your usual routine when writing new tests is to open the browser developer tools and search for CSS selectors to add into your tests. In that case, you are delighted to know Playwright supports recording and code generation via a command-line tool.&lt;/p&gt;

&lt;p&gt;If we want to record actions done in the English Wikipedia, for example, it's as easy as running the following line in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx playwright-cli codegen https://en.wikipedia.org &lt;span class="nt"&gt;-o&lt;/span&gt; test.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using the &lt;code&gt;npx&lt;/code&gt; tool part of Node.js, this command downloads the Playwright CLI tool and opens your browser in a recording mode. The &lt;code&gt;-o test.js&lt;/code&gt; part is used to stream the generated code to standard output (your terminal) and save it to a file.&lt;/p&gt;

&lt;p&gt;In the browser, let's now fetch a random article from Wikipedia, "read" it, and navigate back to the main page. After closing the recording, we have received this code ready to be saved for further inspection.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;playwright&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;headless&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newContext&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Open new page&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Go to https://en.wikipedia.org/wiki/Main_Page&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://en.wikipedia.org/wiki/Main_Page&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Click text="Random article"&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text="Random article"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// assert.equal(page.url(), 'https://en.wikipedia.org/wiki/San_Vittore_Prison');&lt;/span&gt;

  &lt;span class="c1"&gt;// Click //a[normalize-space(@title)='Visit the main page']&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;//a[normalize-space(@title)=&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;Visit the main page&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// assert.equal(page.url(), 'https://en.wikipedia.org/wiki/Main_Page');&lt;/span&gt;

  &lt;span class="c1"&gt;// ---------------------&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;close&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;What is incredibly impressive is that the code is clearly structured, annotated, and wrapped as an asynchronous instantly invocated function. It even suggests places for writing test expectations with &lt;code&gt;assert.equal()&lt;/code&gt;. If you have Playwright installed globally or in a project, you may repeat the previous actions by running &lt;code&gt;node test.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;From here, you likely want to fine-tune the selectors to be more user-friendly, which is a trivial task given your application has a sensible frontend structure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Portability and Automation
&lt;/h3&gt;

&lt;p&gt;If you work on a general-purpose test suite, you may sprinkle a little bit of portability into the cake by using &lt;a href="https://playwright.dev/docs/docker/README"&gt;the official Docker image&lt;/a&gt; provided by Microsoft.&lt;/p&gt;

&lt;p&gt;Writing tests is never enough if they only run locally. Playwright supports most of the Continuous Integration solutions out of the box, including &lt;strong&gt;Travis&lt;/strong&gt;, &lt;strong&gt;Jenkins&lt;/strong&gt;, &lt;strong&gt;Circle CI&lt;/strong&gt;, and &lt;strong&gt;GitHub Actions&lt;/strong&gt;. For the latter, you may also use &lt;a href="https://github.com/microsoft/playwright-github-action"&gt;the official GitHub Action&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing Remarks
&lt;/h2&gt;

&lt;p&gt;Lately, I've started working with an extensive e-commerce application where a robust end-to-end testing suite is worth every penny. We have established an Azure cloud platform development environment and plan to run tests nightly and after deploying a new application version. This lifts tons of manual testing and stresses off our backs.&lt;/p&gt;

&lt;p&gt;With Playwright, I drafted an initial test suite by recording a user flow and refactored it to page object models. After a couple of hours, I had a full-featured test suite which works as a regression test when I want to ensure my changes haven't broken anything. I have a new hammer, and I intend to use it everywhere.&lt;/p&gt;

&lt;p&gt;Have you used Playwright for testing your application? Let me know in the comments how it went.&lt;/p&gt;




&lt;p&gt;&lt;small&gt;&lt;span&gt;Photo by &lt;a href="https://unsplash.com/@dtravisphd?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;David Travis&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/testing?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/span&gt;&lt;/small&gt;&lt;/p&gt;

</description>
      <category>testing</category>
      <category>productivity</category>
      <category>tooling</category>
    </item>
    <item>
      <title>The Unsurprising Truth about What Motivates Developers</title>
      <dc:creator>Niko Heikkilä</dc:creator>
      <pubDate>Tue, 27 Oct 2020 10:02:38 +0000</pubDate>
      <link>https://dev.to/futurice/the-unsurprising-truth-about-what-motivates-developers-3f93</link>
      <guid>https://dev.to/futurice/the-unsurprising-truth-about-what-motivates-developers-3f93</guid>
      <description>&lt;p&gt;Throughout the observed history, human motivation has adapted to demands and economies of the current age. The first establishment, Motivation 1.0, provided us with the tools for surviving in a harsh and dangerous environment – why care about anything else than obtaining food and not being eaten by predators? During the industrialization era, Motivation 2.0 enabled considerable leaps in manufacturing and productivity with its system of rewards and punishments ultimately paving the way for modern digital work. However, to develop as a creative craftsman instead of a wage slave, we need Motivation 3.0.&lt;/p&gt;

&lt;p&gt;Effectively, Motivation 3.0 replaced the carrots and sticks of extrinsic &lt;em&gt;if-then-else&lt;/em&gt; stimulants with &lt;strong&gt;autonomy&lt;/strong&gt;, &lt;strong&gt;mastery&lt;/strong&gt;, and &lt;strong&gt;purpose.&lt;/strong&gt; The three branches – or suffice to say a framework – for intrinsic motivation laid out by author &lt;strong&gt;Daniel H. Pink&lt;/strong&gt; in their book &lt;em&gt;&lt;a href="https://www.danpink.com/books/drive/"&gt;Drive: The Surprising Truth About What Motivates Us&lt;/a&gt;&lt;/em&gt; (spoiler: it wasn't &lt;em&gt;that&lt;/em&gt; surprising).&lt;/p&gt;

&lt;p&gt;The AMP framework – as I call it – has been my professional companion for long. In this post, I share what the framework is made of, and how it might help you to become the best version of yourself.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You might get more out of this post by reading the book first. Nevertheless, I try to explain the terminology used in the book in the common tongue.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Baselines
&lt;/h2&gt;

&lt;p&gt;According to Pink, before diving deeper into the AMP framework, we need to understand the very building blocks of motivation: our baseline. For developers, they are the pre-requisites for even signing a contract, which includes competent salary, functional equipment, IT budget, ability to work remotely, and other benefits. Granted, these examples are slightly biased towards my own preferences, but it doesn't make them less important.&lt;/p&gt;

&lt;p&gt;It's worth emphasizing – especially to recruiters – that proper baseline can attract people to &lt;em&gt;start&lt;/em&gt; working with you while successful adoption of the AMP framework will make them &lt;em&gt;continue&lt;/em&gt; the work without fear of notice.&lt;/p&gt;

&lt;p&gt;Keep on reading if you want to increase employee retention. Before publishing that new rockstar position in your company, get your baselines straight.&lt;/p&gt;

&lt;h2&gt;
  
  
  Autonomy
&lt;/h2&gt;

&lt;p&gt;🧠 &lt;em&gt;Assume the command of your work and career.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As a developer, I need to have freedom over task (what I do), time (when I do it), team (who I do it with), and technique (how I do it).&lt;/p&gt;

&lt;p&gt;The work we do is usually formed around customer needs which can feel inflexible until we come up with a solution: a big task eventually split into smaller tasks. As long as we don't procrastinate, it's all right to work on those tasks whenever we feel a surge of productivity. These are the most accessible parts of developer autonomy. However, we often can't decide the team we belong to or the techniques such as programming languages, frameworks, and platforms we work with unless we land in a new project.&lt;/p&gt;

&lt;p&gt;Some companies have strict guidelines on who can do what. Product owners define what is done, and architects plan how it's done without asking for second opinions. Remote work may be banned, and unmotivated people are crammed into cubicles from 8 to 16 every day. Team compositions are homogenous, and developers are permitted only to work on the part of the project lifecycle. There's only one way of developing software set in stone years ago.&lt;/p&gt;

&lt;p&gt;Real autonomy can only be achieved by running things on your own, which often means running your own business. Fortunately, for those unwilling to sacrifice the comfortability of a stable payroll, many companies help to boost autonomy by offering a selection of viable projects, flexible working styles, diverse teams and trust their developers pick the right tool for the right situation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mastery
&lt;/h2&gt;

&lt;p&gt;📚 &lt;em&gt;Through autonomy, we direct ourselves towards mastery.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To create engaging software, developers need to feel engaged in their work. Some of us are familiar with &lt;strong&gt;Mihaly Csikszentmihalyi's&lt;/strong&gt; concept of &lt;em&gt;flow&lt;/em&gt;: the state of mind where our skills meet the level of challenge, and we sink into the task with an outcome of deep satisfaction. You've probably experienced it quite often.&lt;/p&gt;

&lt;p&gt;The topic of learning and improving skills is tricky since every one of us has a unique approach to it. Some read books, others listen to podcasts. Some might see the light in a couple of videos, while some need to work on real projects. Therefore, people should be given ample time to hone their skills the way they see fit. It's sad to witness the "features first, learning second" mentality many companies suffer from.&lt;/p&gt;

&lt;p&gt;My previous employer had a concept of Freestyle Fridays where the developers spent a whole Friday working on their own projects and presenting them to others. Some companies allocate 20 % of developers' time for non-billable work which is a sweet spot for learning.&lt;/p&gt;

&lt;p&gt;Extreme programming practices such as test-driven development (a great source of flow) and pairing – ideally in junior-senior pairs – provide significant results when learning occurs mostly in projects.&lt;/p&gt;

&lt;p&gt;As I wrote in &lt;a href="https://nikoheikkila.fi/blog/fight-the-inner-impostor-with-just-in-time-learning/"&gt;my previous post&lt;/a&gt;, mastery is an asymptote, and we should not fool ourselves, thinking we can reach the peak. Our abilities are always &lt;em&gt;infinitely improvable&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Make sure you can block time off for improving skills, and your management supports it. Participate in projects where your skills accumulate fastest. Learn things just-in-time to preserve sanity. Focus on fundamental skills and use them to solve problems in more specialized domains.&lt;/p&gt;

&lt;h2&gt;
  
  
  Purpose
&lt;/h2&gt;

&lt;p&gt;📣 &lt;em&gt;Through mastery, we can contribute better to a broader vision and leave a lasting impact.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Nearly every company claims to have a vision, mission, and a strategy to fulfil those. Many of these companies exhibit visually breathtaking landing pages yet in meetings speak only of the last quarters turnover.&lt;/p&gt;

&lt;p&gt;Ideally, our first priority as developers is to satisfy the customer, which enables us to keep our job, but it's not a purpose as such. The purpose lies beyond our work, and it can be anything from reducing the effects of climate change, helping people in the developing countries, or ensuring the local theatre can more easily offer a dose of culture.&lt;/p&gt;

&lt;p&gt;The older we grow, the more concrete the handprint we wish to leave behind. Our careers should not be a necessary evil to pass by before we can write that groundbreaking novel during our retirement years. We should do it now, while on top of our game. If your work doesn't fulfil any purpose in your life, stop wasting your time and quit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding Your Drive
&lt;/h2&gt;

&lt;p&gt;The AMP framework unlocks a path to a promising career, but to fully reap its benefits, you also need to study the path of &lt;a href="https://nikoheikkila.fi/blog/death-of-the-production-line-and-factory-workers-attitude/"&gt;&lt;strong&gt;software craftsmanship&lt;/strong&gt;&lt;/a&gt;. A direction opposed to the production line and factory worker attitude with professionalism, pragmatism, and pride as motivators.&lt;/p&gt;

&lt;p&gt;It's best to seek motivation from your current job, but in case it's not possible, there are alternatives for developers. A well-researched application of the AMP framework is open-source development. Developing and maintaining open-source projects, especially your own, offers a world of highest autonomy where everything can be harnessed for mastery, and nothing thwarts the journey to a glorious purpose.&lt;/p&gt;

&lt;p&gt;Then again, you may find your drive elsewhere, but science has shown that, wherever it may be, the AMP framework is often the culprit. Even a construction worker can feel motivated by building a children's hospital with their chosen tools in a flow state of mind.&lt;/p&gt;

&lt;h2&gt;
  
  
  Maintaining the Drive
&lt;/h2&gt;

&lt;p&gt;After finding your drive, you should make regular checks regarding the level of autonomy, mastery, and purpose at your work. Have the levels increased or decreased during the past six months?&lt;/p&gt;

&lt;p&gt;To close, here are a couple of questions for your next feedback and development session.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What has changed in your work regarding the task, time, team, and technique? Has the change been for better, or for worse?&lt;/li&gt;
&lt;li&gt;Have you noticed any new obstacles preventing your autonomy? How can you finish your work without hanging onto dependencies?&lt;/li&gt;
&lt;li&gt;When was the last time you learned a new skill? Were you able to apply it to a particular problem?&lt;/li&gt;
&lt;li&gt;Have you got better in any of the skills? To what extent?&lt;/li&gt;
&lt;li&gt;How often have you experienced a flowing state of mind? How can you form regular habits of those moments?&lt;/li&gt;
&lt;li&gt;When was the last time you made an impact on someone or something (colleague, manager, client, society, etc.)?&lt;/li&gt;
&lt;li&gt;What purposes are you serving through your current projects? Do they align with your views of the world?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feel free to answer in the comments as well!&lt;/p&gt;




&lt;p&gt;&lt;small&gt;Photo by Max Duzij on Unsplash.&lt;/small&gt;&lt;/p&gt;

</description>
      <category>motivation</category>
      <category>career</category>
      <category>books</category>
      <category>leadership</category>
    </item>
    <item>
      <title>Fight the Inner Impostor with Just-In-Time Learning</title>
      <dc:creator>Niko Heikkilä</dc:creator>
      <pubDate>Sat, 17 Oct 2020 15:52:53 +0000</pubDate>
      <link>https://dev.to/futurice/fight-the-inner-impostor-with-just-in-time-learning-b3i</link>
      <guid>https://dev.to/futurice/fight-the-inner-impostor-with-just-in-time-learning-b3i</guid>
      <description>&lt;p&gt;This post might come off like a weird spiritual tip for a developer, but bear with me a minute...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;&lt;a href="https://en.wikipedia.org/wiki/Santosha"&gt;Santosha&lt;/a&gt;&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A word I came across in yoga that fits very aptly into the 21st-century knowledge work. It originates from Sanskrit language and roughly translates to "&lt;em&gt;complete acceptance"&lt;/em&gt;, which we all should embrace.&lt;/p&gt;

&lt;p&gt;As developers, we are frequently caught in a feeling of incompleteness. Our work is never done. The code we write is far from perfection. There's always room for improvement by refactoring, or even rewriting complete pieces of the software. Moreover, masses of new frameworks and libraries catch our eyes daily. What about that next certificate or promotion?&lt;/p&gt;

&lt;p&gt;The problem here emerges not from the practice of skills towards mastery, but the constant feeling that something is missing in our lives. When I rise on my career level, master the Kotlin programming language, and read these 50 books about object-oriented programming, I shall finally be happy!&lt;/p&gt;

&lt;p&gt;Well, no, I won't. After that, I shall be just as unsatisfied and even severely depressed in the worst case.&lt;/p&gt;

&lt;p&gt;Let it go. Don't plunge headfirst learning all the things that won't benefit you now. Instead, accept that your knowledge is never complete. The real mastery is an asymptote: a graph approaching but never reaching a point.&lt;/p&gt;

&lt;p&gt;To fight the anxiety of needing to know everything, consider doing things &lt;strong&gt;just-in-time&lt;/strong&gt;. Learn new techniques when they are necessary for your work. If an upcoming client uses a technology unfamiliar to you, grab a few quick courses beforehand. Rewrite that unstable component when its maintenance produces more risk than revenue. Write tests, not too many, mostly integration tests. Most importantly, follow the paths of &lt;a href="https://codewithoutrules.com/2020/09/18/ydniy/"&gt;&lt;strong&gt;YAGNI&lt;/strong&gt; (You Aren't Gonna Need It) and &lt;strong&gt;YDNIY&lt;/strong&gt; (You Don't Need It Yet)&lt;/a&gt;. In the end, you have wasted zero time and spared your sanity.&lt;/p&gt;

&lt;p&gt;However, modern society often is against just-in-time learning. We are expected to master a broad set of skills to be hireable to a new position or sellable to a client. We need to shift our thinking from masters of all trades to just-in-time specialists. Being a generalist is a good start, but specializing is required for solving real-world problems.&lt;/p&gt;

&lt;p&gt;A practical example of just-in-time learning from my career is &lt;a href="https://github.com/paytrail/documentation"&gt;the integration documentation revamp&lt;/a&gt; I made for my previous employer. I wanted to find a fast and stable platform which would make writing technical documentation easy and fun for developers. While investigating this, I stumbled upon &lt;em&gt;Hugo&lt;/em&gt;, which led me to learn about its internals, and how its static output integrates with &lt;em&gt;Netlify&lt;/em&gt; and &lt;em&gt;GitHub Flow&lt;/em&gt; also allowing non-developers to give rapid feedback by viewing preview deployments from pull requests. Similarly, I knew the basics of &lt;em&gt;React&lt;/em&gt; and &lt;em&gt;GraphQL&lt;/em&gt; but didn't dive into those before starting to write my personal blog with &lt;em&gt;Gatsby&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Many years ago, I spent time reading arbitrary online tutorials without actually having a problem to solve. Today, by solving narrow use cases just in time, I've gained confidence that I'm on the right path. Having a track record of concrete solutions, I'm also completely satisfied (&lt;em&gt;santosha&lt;/em&gt;) with my current situation while understanding there's still a lot for me to learn.&lt;/p&gt;




&lt;p&gt;&lt;small&gt;Photo by Road Trip with Raj on Unsplash.&lt;/small&gt;&lt;/p&gt;

</description>
      <category>motivation</category>
      <category>mentalhealth</category>
      <category>productivity</category>
      <category>career</category>
    </item>
    <item>
      <title>First Four Weeks at Futurice</title>
      <dc:creator>Niko Heikkilä</dc:creator>
      <pubDate>Tue, 15 Sep 2020 13:08:40 +0000</pubDate>
      <link>https://dev.to/futurice/first-four-weeks-at-futurice-edo</link>
      <guid>https://dev.to/futurice/first-four-weeks-at-futurice-edo</guid>
      <description>&lt;p&gt;Earlier in the summer of 2020, I made a bold decision to refresh and broaden my career from a product company to software consultancy.&lt;/p&gt;

&lt;p&gt;I was yearning to work in different projects after having spent nearly four years with backend development on e-commerce domain. As many of us know, the secret to shining job hunting is to have a satisfying current position yet hunger for more which effectively removes desperation from the formula and lets you easily show off the best you.&lt;/p&gt;

&lt;p&gt;After measuring my options, I eventually ended up at &lt;a href="https://promise.tammerforce.com/en/"&gt;&lt;strong&gt;Futurice&lt;/strong&gt;&lt;/a&gt; as a full-stack developer in August. Here's the story of my first month there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Hired
&lt;/h2&gt;

&lt;p&gt;Futurice had been popping in my feeds for several months, and I had heard good things about them. Marketing keywords like trust, transparency, and care caught my interest. After reading their &lt;strong&gt;&lt;a href="https://futurice.com/blog/the-futurice-culture-handbook"&gt;Culture Handbook&lt;/a&gt;&lt;/strong&gt;, I decided to give them a shot.&lt;/p&gt;

&lt;p&gt;With a curious attitude, I whipped up a job application and a brief paper of my selected open-source projects. To my surprise, I was contacted by one of their developers, and we agreed on an interview. Yes, you read it right, no external headhunting agents were found here. With people close to my profession at the other end of the table, I could ask honest questions about life in Futurice and whether it was the right choice for me.&lt;/p&gt;

&lt;p&gt;At the first interview, we mutually agreed on many things. From there, I went on to have a technical discussion — with two different developers — where I showed them my projects. We talked about typical problems encountered by developers and how to solve them. The session was calm and informal, and I certainly didn't have to solve a recursive Hanoi tower on a whiteboard with trembling hands while being intensively stared at. Having conducted a couple of technical interviews myself, I thanked them for this pleasant opportunity free from anxiety and stress.&lt;/p&gt;

&lt;p&gt;The third and final interview — they sure invest time in applicants — was with two &lt;em&gt;tribe chiefs&lt;/em&gt;. This was more of a complementary check in the form of another calm talk about where I was in my career and what could be the next path. I inquired about learning and personal development opportunities. I got to know better about their &lt;a href="https://spiceprogram.org/"&gt;Spice Program&lt;/a&gt;, Ultimate Learning Platform, and compensated certification studies. I keep a spreadsheet for calculating the optimal job opportunities and Futurice scored relatively high at this point.&lt;/p&gt;

&lt;p&gt;A couple of days went by, and I found an offer for a full-stack developer position in my inbox. Attached was a letter telling I was keenly wanted to join their team.&lt;/p&gt;

&lt;p&gt;The whole hiring process was a breeze with all the interviews being held remotely via Google Meet. Despite talking through video, I was able to get an accurate vision of their daily life which didn't differ much with the reality witnessed after visiting the office for the first time. But why should I tell you more if you can see it for yourself in the video below?&lt;/p&gt;

&lt;h2&gt;
  
  
  Life as a Tammerforcean
&lt;/h2&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/tMTa5GALU6E"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Having said goodbye to &lt;strong&gt;Paytrail&lt;/strong&gt; in Jyväskylä and getting ample time relocating to Tampere, I cycled to the office located in the heart of the town next to Tammerkoski rapids.&lt;/p&gt;

&lt;p&gt;I was greeted by my supervisor and people from the Human Care team (people are not &lt;em&gt;resources&lt;/em&gt; here), grabbed coffee, and received the laptop and phone. I was also assigned a &lt;em&gt;FutuBuddy&lt;/em&gt;, whose role was to tour me through the office and introduce me the ways of working during the first week.&lt;/p&gt;

&lt;p&gt;Some companies pick the first project for a newcomer to work from the day one accepting no alternative points of view. However, Futurice presented me with a selection of viable projects to see what was the most interesting. I chose to join a team responsible for the maintenance and small-scope development of existing projects. This line of work was rather familiar to me, and I mused it might help me settle myself best to a new environment both culturally and technically.&lt;/p&gt;

&lt;p&gt;Around 80 people are working in Tammerforce, but due to the pandemic I've only seen and greeted no more than half of them, I think. A large number of people work mostly from home and that's ultimately natural: you don't have to ask permission nor worry about how your teammates feel about it. The same goes for the work in general, you're trusted to bring your best work forwards, no matter where or how you do it.&lt;/p&gt;

&lt;p&gt;A particular manifestation of trust in Futurice is the relationship with your supervisor. The concept is as you would expect in a modern technology organization, straying heavily away from bosses and subordinates in favour of mentors and mentees. The supervisor is there to guide you in your career and provide daily support instead of watching you at work. We've already had a couple of fruitful chats. Additionally, we also have a unique retro session twice a year for making sure my output, career level and salary are correctly aligned. My current career level that I've voluntarily shared with others is that of a mid-tier developer, but I'm targeting for a more senior level in the next sessions.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Does It Feel Now?
&lt;/h2&gt;

&lt;p&gt;This is a question I'm asked by my colleagues almost daily — and it's okay, don't think otherwise.&lt;/p&gt;

&lt;p&gt;The life in consultancy sure is a lot different than in a single-product organization. Most notably, you're not limited to the one and only technology stack. A lot of the choices depend on the client you're serving and who's experienced with what in your close team. I ended up working with React, TypeScript, and Node which are tightly in my comfort zone, but I could have also sought for Golang, Java, or Kotlin projects. There are even some Clojure shenanigans for all the &lt;em&gt;lispy&lt;/em&gt; people. Anything goes as long as I don't find myself locked alone in a closet working with an ancient COBOL infrastructure!&lt;/p&gt;

&lt;p&gt;The technological variety has its downsides as well. People work in many different projects, so mutual conventions are rarely established, and people might choose tools on a whim. Two teams could be using, for example, AWS services in very different, although practical, ways. Having an exceptional taste for knowledge sharing, I'm eagerly waiting to formulate and share the best practices between projects and trying to improve the ways of working. So far, the people have been supporting towards that end.&lt;/p&gt;

&lt;p&gt;Perhaps the sunniest side in Futurice has been to surround yourself with like-minded and more experienced developers. I've got a lot to learn from them — and they from me. Working with cloud services in an organic DevOps culture where developers, designers, and business people bump their fists to design, develop, and deploy cloud products for clients is a thing I won't be giving up easy. We are communicating with clients daily, and we don't have separate QA or Ops departments because we can be trusted to handle the whole development pipeline like eating a candy.&lt;/p&gt;

&lt;p&gt;To answer the question above, I feel fine. Yeah. No problems.&lt;/p&gt;




&lt;p&gt;&lt;small&gt;&lt;em&gt;Cover image by Tiia Monto under CC BY-SA 3.0 license.&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

</description>
      <category>career</category>
      <category>learning</category>
      <category>motivation</category>
      <category>interview</category>
    </item>
  </channel>
</rss>
