<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Ari Koponen</title>
    <description>The latest articles on Forem by Ari Koponen (@apkoponen).</description>
    <link>https://forem.com/apkoponen</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%2F341672%2F1a9fcadb-7312-4a42-9a41-404653df1b54.jpg</url>
      <title>Forem: Ari Koponen</title>
      <link>https://forem.com/apkoponen</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/apkoponen"/>
    <language>en</language>
    <item>
      <title>6 ways developers can learn more at work</title>
      <dc:creator>Ari Koponen</dc:creator>
      <pubDate>Wed, 05 Jan 2022 00:00:00 +0000</pubDate>
      <link>https://forem.com/apkoponen/how-developers-can-learn-on-company-time-3j9</link>
      <guid>https://forem.com/apkoponen/how-developers-can-learn-on-company-time-3j9</guid>
      <description>&lt;p&gt;We know from multiple studies (e.g., &lt;a href="https://files.eric.ed.gov/fulltext/ED501596.pdf"&gt;1&lt;/a&gt;, &lt;a href="https://onlinelibrary.wiley.com/doi/pdf/10.1111/1748-8583.12071"&gt;2&lt;/a&gt;, &lt;a href="https://trepo.tuni.fi//bitstream/handle/10024/133747/10_1108_JWL_12_2020_0184_1.pdf"&gt;3&lt;/a&gt;) that learning at work improves both job satisfaction and organizational commitment. Both of these contribute to higher productivity and lower turnover.&lt;/p&gt;

&lt;p&gt;Many companies use personal learning budgets and some type of "5%/10%/20% time" policy to encourage learning. These are useful but highly dependent on personal motivation and learning habits. It’s often hard for software developers to prioritize learning over their busy day-to-day work. For example, &lt;a href="https://www.businessinsider.com/google-20-percent-time-policy-2015-4"&gt;only around 10% of Googlers are using their famous "20% time" policy.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The best way for a software developer to learn is by having a role and a project where they can learn through their day-to-day tasks. That’s why companies should build a culture where learning is part of the developers’ job instead of an "add-on." This helps with motivation and allows people with little free time — like the parents of small children — to continue learning.&lt;/p&gt;

&lt;p&gt;Here are six proven ways to do that.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. (Good) code reviews
&lt;/h2&gt;

&lt;p&gt;Code reviews offer a great opportunity to learn because the feedback you get in a code review is highly contextual and often very specific. When you discuss why one solution is better than another, you can develop your professional intuition in a way that’s very hard to achieve otherwise.&lt;/p&gt;

&lt;p&gt;Learning happens in code reviews only if they’re done well. If your team suffers from rubber-stamping "LGTM" code reviews and you want to improve them, you can check this &lt;a href="https://www.swarmia.com/blog/a-complete-guide-to-code-reviews/"&gt;complete guide to code reviews&lt;/a&gt; from Kimmo Brundfeldt.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Pair programming
&lt;/h2&gt;

&lt;p&gt;During pair programming, you watch another person write code or hear them discuss a solution. This allows you to pick up practical tips on improving your work (e.g., how to use your editor). You also learn new ways of thinking and solving problems.&lt;/p&gt;

&lt;p&gt;Pair programming &lt;a href="https://collaboration.csc.ncsu.edu/laurie/Papers/XPSardinia.PDF"&gt;costs less than most people think&lt;/a&gt;. In addition to improving technical skills, it also improves design quality, reduces defects, reduces staffing risk, improves team communication, and is often considered more enjoyable than working alone.&lt;/p&gt;

&lt;p&gt;Tuple’s excellent &lt;a href="https://tuple.app/pair-programming-guide/"&gt;Pair Programming Guide&lt;/a&gt; covers everything from getting started with pair programming to scientific research on the subject.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. WIP limits
&lt;/h2&gt;

&lt;p&gt;Developers often prefer to work alone on tasks and issues, even when the work could be split into smaller tasks and worked on side-by-side. Developers feel more productive when they don’t have to deal with the "overhead" of coordinating with others. However, this often leads to more siloing of knowledge and less knowledge sharing within the team.&lt;/p&gt;

&lt;p&gt;Many people know work-in-progress (WIP) limits only as a Lean/Kanban to improve the flow of work. But having a sensible WIP limit improves learning through collaboration. As an added bonus, it also leads to much better code reviews.&lt;/p&gt;

&lt;p&gt;You can take a look at this blog post on &lt;a href="https://www.swarmia.com/blog/issue-cycle-time/#2-working-on-too-many-things-at-the-same-time/#wip-limits"&gt;decreasing issue cycle time&lt;/a&gt; to figure out if you’re working on too many things simultaneously and would benefit from WIP limits.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Design documents
&lt;/h2&gt;

&lt;p&gt;Design documents, RFCs, or just "documented plans" are a great way to enable learning on the job. When done well, the document explains why the chosen solution is the best from both product and technical perspectives. It considers the tradeoffs of different approaches and highlights the most important non-functional requirements that contribute to the solution, like accessibility, performance, and user experience.&lt;/p&gt;

&lt;p&gt;Reading a highly contextual document like this allows developers to learn how other — often more experienced — people think about technical decisions. The developers writing the design documents also learn valuable skills: system design, communicating technical decisions, and how to motivate and inspire peers to adopt a solution.&lt;/p&gt;

&lt;p&gt;To get started, learn &lt;a href="https://www.swarmia.com/blog/why-product-teams-should-plan-together/"&gt;how to plan together as a team&lt;/a&gt; and check out Gergely Orosz’s &lt;a href="https://blog.pragmaticengineer.com/scaling-engineering-teams-via-writing-things-down-rfcs/"&gt;article on design documents&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Study groups
&lt;/h2&gt;

&lt;p&gt;Organizing study groups within your engineering organization is a great tool for peer-to-peer learning. This is especially true when you have more than a handful of developers spread across multiple teams. Trying to apply books or courses to day-to-day work is not always easy — and may lead to people "trying out stuff" on your codebase just to learn. When people who work in the same context discuss the material, it’s easier to identify valuable learnings.&lt;/p&gt;

&lt;p&gt;Often, the best part of study group meetings is going beyond the material by having people share their own experiences and different angles on the subject. This is especially true for books/materials that cover high-level principles (e.g., books like "Domain-Driven Design" and "Working with Legacy Code.")&lt;/p&gt;

&lt;p&gt;I’m planning on writing a full guide on running great peer study groups (let me know if you’re interested in reading it.) For now, these are the steps we used at a company with 350+ developers to run study groups at scale:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Find a topic, a book, or a course that 4-10 developers in your organization are interested in. Optionally, find 1-2 "expert members" from your company who can participate. Choose a person responsible for organizing the study group meetings.&lt;/li&gt;
&lt;li&gt;Set up a 30-60 minute meeting with a weekly recurring calendar invite for the study group. The larger the group, the more time you need. The meeting can be an extended company-paid lunch.&lt;/li&gt;
&lt;li&gt;For every meeting, choose a section of the material that everyone can go through beforehand. E.g., for books, we used less than 50 pages. Participants are expected to go through this material at their own pace in between meetings.&lt;/li&gt;
&lt;li&gt;Start the meetings by having everyone go through their notes or thoughts on the material. Discuss.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When the material for the study group is highly related to the participants’ current project, they can apply the learnings almost immediately. Other materials will usually pay dividends in the long run.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Peer-to-peer sharing
&lt;/h2&gt;

&lt;p&gt;One of the best ways to get excited about learning is through the people around you. This is why building a culture of sharing learnings with peers is so important. For example, you can watch engineering talks together, share links to interesting content on company Slack, or have sessions where team members share their knowledge on some topic.&lt;/p&gt;

&lt;p&gt;Not everyone will read every excellent blog post on Slack or attend every session, but whenever they do, they’re likely to learn something useful. Positive learning experiences create a virtuous cycle and motivate people to learn more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Share your own experiences
&lt;/h2&gt;

&lt;p&gt;What has worked for you? Have you tried out any of these practices? How can software developers learn on company-time?&lt;/p&gt;

</description>
      <category>career</category>
      <category>discuss</category>
      <category>programming</category>
    </item>
    <item>
      <title>100 Tips on Software Developer Productivity</title>
      <dc:creator>Ari Koponen</dc:creator>
      <pubDate>Wed, 15 Dec 2021 09:40:44 +0000</pubDate>
      <link>https://forem.com/apkoponen/100-tips-on-software-developer-productivity-36if</link>
      <guid>https://forem.com/apkoponen/100-tips-on-software-developer-productivity-36if</guid>
      <description>&lt;p&gt;&lt;em&gt;I've developed software professionally for the past 10 years. Here's a list of 100 things I've learned about &lt;strong&gt;software developer productivity&lt;/strong&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;ol&gt;
&lt;li&gt;&lt;p&gt;At the beginning of your career, learning to "code fast" significantly impacts your productivity. Once you're "good enough," the impact substantially diminishes. Pay attention to when this happens.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Don't be too proud to try and" hack your mind." Many productivity tips that sound patronizing actually work.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Write your tasks down and use a to-do list.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Having a tangible goal will make you move faster.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Procastination is normal. Start by writing a single line.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;What works for someone else might not work for you.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Try Pomodoro. It might work for you.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Learn what makes you productive.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Set boundaries that enable you to work in a way that makes you productive.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Strong intrinsic motivation and curiosity trumps all productivity hacks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Learn how to tap into your intrinsic motivation. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pair program and watch other people code. You might pick up a skill or two that help you work faster.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Your intuition affects your productivity more than you know. Pay attention to cultivating a good intuition.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Learning out of a bad habit takes work. Invest in learning good habits.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Saying "yes" to everything will destroy your productivity. You need a personal WIP limit.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Being obsessed about your work will make you temporarily more productive but burn you out if stretched for too long.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Physical exercise, eating healthy, and sleeping well do not take away, but &lt;em&gt;contribute&lt;/em&gt; to your productivity.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Take regular breaks (e.g., 10 minutes every 50 minutes). &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Trying to work in too long stretches will teach your brain to start wandering off to memes and hot takes on Twitter.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Make sure you have enough focus time for deep work and "flow." &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Disable all the notifications you can.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Being in "flow" makes you feel productive but does not always mean you're making progress.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can spend hours polishing an abstraction but deliver zero customer and business value.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Being productive does not mean you're making an impact. Work hard on things that matter.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The more experience I get, the more I'm interested in improving how my team or organization works together vs. optimizing my individual productivity.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When solving technical problems that require collaboration, the bottleneck for individual developer productivity is not your ability to write code but the team's ability to create viable solutions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Most problems in building great software products require collaboration. Your ability to collaborate will become a bottleneck.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You will be more productive in some teams/companies than others.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Eventually, the bottleneck for your productivity will be the team and organization surrounding you.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Developer productivity and compensation don't often go hand in hand.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Some companies will compensate you more for the same work than others.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The best "10x engineers" are the ones who 10x the team and people working around them.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Delegate and help other people. Learn to celebrate their success.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Learn to use automation. Removing errors and reducing cognitive load has a more significant impact on your productivity than "working faster."&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Developer experience and ergonomics matter.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Automatically format your code with a deterministic code formatter like Prettier or Black.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use a linter. Learn how to write custom rules to match the way your team works.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Write automated tests. Life is too short for constantly fixing trivial bugs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Setting up a Continuous Integration (CI) pipeline is likely easier than you think. Learning how to do it will save you time in every future software project.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Keep feedback loops fast.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Automatically deploy to production.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Keep your build times low.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Make sure developers can run the test suite on their computers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enable automatically running related tests on file changes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use feature flags to allow shipping work faster to production.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You might not need a staging environment.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You might benefit from a staging environment.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It depends.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Developers using Vim look like they're much more productive than the rest.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Don't always pick the tasks that make you feel most productive. It will stagnate your learning and make you feel that you're not moving forward.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Prioritize reviewing code. It will help your team stay productive.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Every time a developer prioritizes reviewing code, a new star is born in the Torvalds &amp;amp; Carmack galaxy.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Being "agile" has very little practical meaning.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Calculating velocity and story points rarely help teams improve their productivity.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Small, practical change based on data trump "agile transformation."&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The goal of planning is to split the elephant into bite-sized pieces.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Developers often prefer working alone on large tasks for long periods.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Developers working alone on large tasks for long periods rarely result in the smallest possible increment being shipped fast.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Working together on a task is often less productive in the short run.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Working together on a task improves learning and is often more productive in the long run.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;With large codebases, developer productivity largely depends on their knowledge of the system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Splitting large codebases into modules helps new developers become productive faster.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Modular architecture only works if the API contracts are clear and modules are truly decoupled from each other.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sometimes, a developer is more productive than others only because they have more knowledge and power.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A single developer owning a disproportionate amount of knowledge and power in a team will lead to some developers underperforming.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A "10x engineer" born out of owning a disproportionate amount of knowledge and power is an anti-pattern and will lead to capable developers leaving your company. Avoid it at all costs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Measuring developer productivity only through activity (e.g. # of commits or PR reviews) does not work.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Trying to condense developer productivity into a single metric will fail and make developers lose their trust in the company's management.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Measuring individual developer productivity is hard. If you try to do that, you will likely lose more than gain.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When trying to measure developer productivity, data quality is crucial. Bad decisions made based on poor data are costly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;There is much more reliable research on software developer productivity than most developers realize.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Focusing on team and organizational level developer productivity will yield the best results.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Reading the book "Accelerate" and understanding the SPACE framework will give you a good start to understanding development team productivity.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When measuring developer productivity, you should have multiple metrics that complement and challenge each other.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The subjective experience of developers is valuable data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Only relying on the subjective experience of developers is not enough. Especially when trying to improve development productivity across the organization.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Improving software team productivity is largely about trying to improve the flow of work while keeping quality high and working at a sustainable pace.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Measuring your team's cycle time and deployment frequency will help you improve your flow of work.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Knowing how much time your team spends fixing bugs vs. feature work will help you prioritize writing tests, fix technical debt, and keep your quality high.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Developing software is a marathon, not a sprint. Optimize accordingly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Teams should be in charge of improving their own productivity and have access to the necessary metrics and tools.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Individual developers benefit from understanding what affects their teams' overall productivity.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Most teams try to do too many things at the same time. You need a WIP limit.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;WIP limits improve collaboration and help you ship the most important stuff.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;WIP limits reduce procrastination on hard tasks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;WIP limits combined with good prioritization help individual developers work on things they're not familiar with and encourage learning.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Encouraging learning makes software developers more productive in the long run.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Consistently good work is better than heroics.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Organizations that put heroism above consistently good work perform worse in the long run.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Developers love to measure everything else but their own productivity.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Early attempts at measuring software developer productivity ruined the whole thing for most developers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It is hard to recover from industry trauma.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Don't focus on measuring individual developers if you're trying to improve your company's software development speed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Learning about Lean and Queuing theory is valuable when improving software team productivity.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Consultants have done a great job ruining Lean for the rest of us.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Measuring software team productivity can make engineering teams happier.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Measuring software team productivity only makes teams happier and more productive if they're on board.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If developers are not on board, your metrics will be gamed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Not giving your developers proper autonomy and control over their work will make them unhappy. Do not micromanage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Happy engineers are more productive.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  See and share on Twitter
&lt;/h3&gt;

&lt;p&gt;You can see these tips in Twitter thread here:&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1471028936314339330-817" src="https://platform.twitter.com/embed/Tweet.html?id=1471028936314339330"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1471028936314339330-817');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1471028936314339330&amp;amp;theme=dark"
  }



&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Reduce bias in retrospectives with better data</title>
      <dc:creator>Ari Koponen</dc:creator>
      <pubDate>Wed, 27 Oct 2021 10:31:16 +0000</pubDate>
      <link>https://forem.com/apkoponen/stop-fake-improvements-with-data-driven-retrospectives-41ao</link>
      <guid>https://forem.com/apkoponen/stop-fake-improvements-with-data-driven-retrospectives-41ao</guid>
      <description>&lt;p&gt;&lt;em&gt;Many software organizations run retrospectives based on mostly subjective data collected from the retrospective participants. Studies show this leads to skewed insights that often miss broader organizational goals. One solution is to add a step to your retrospectives in which you present relevant hard data gathered from a wide array of data sources.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I recently wrote a blog post on &lt;a href="https://www.swarmia.com/blog/well-researched-advice-on-software-team-productivity"&gt;well-researched advice on software team productivity&lt;/a&gt; that included a summary of a few excellent studies on the effectiveness of retrospectives. These studies highlight how teams often rely too much on the subjective personal experiences of the team members to collect data for the insights and actions generated from the retrospectives.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[Retrospective] discussions might suffer from participant bias, and in cases where they are not supported by hard evidence, they might not reflect reality, but rather the sometimes strong opinions of the participants.&lt;br&gt;
&lt;cite&gt;&lt;br&gt;
 ~ Lehtinen et al. (&lt;a href="https://link.springer.com/article/10.1007/s10664-016-9464-2"&gt;2014&lt;/a&gt;)&lt;br&gt;
&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Retrospectives focused on the personal experiences of the participants often work as great team-building events. The challenge is that retrospectives based only on subjective data collected from the participants are susceptible to multiple cognitive biases like recency and confirmation bias.&lt;/p&gt;

&lt;p&gt;As these retrospectives often miss some crucial pieces of data, they may result in false insights. False insights result in actions that are at best a waste of time, at worst, a hindrance to the organization's goals.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What a data-driven retrospective looks like&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;There is a multitude of ways you can run a retrospective. This blog post will use the 5-step retrospective structure from the book "Agile Retrospectives." It is one of the most well-known options and is a great, generally applicable approach to running a retrospective. Most retrospectives we've seen teams run use at least some parts of its structure.&lt;/p&gt;

&lt;p&gt;The five steps of the structure are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Set the stage&lt;/strong&gt; : Help people focus on the work at hand. Reiterate the goal. Create an atmosphere where people feel comfortable discussing issues. Get everyone to say a few words.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gather data&lt;/strong&gt; : Collect the data you need to gain useful insights. Start with the hard data like events, metrics, features, or stories completed. Continue with personal experiences and feelings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generate insights&lt;/strong&gt; : Ask "why?" Think analytically. Examine the conditions, interactions, and patterns that contributed to successes or failures. Avoid jumping to the first solution, and consider additional possibilities to make sure you address the actual root cause.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decide what to do&lt;/strong&gt; : Pick the top items from the list of improvements. Decide the actions you are going to get done. Assign the actions to individual people.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Close the retrospective&lt;/strong&gt; : Decide how to document the results, and plan for follow-up. Ask for feedback about the facilitation/retrospective.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--n1PkoHIi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.swarmia.com/static/090bc6f4132597bcf3fec4c4498b849a/00d43/5-step-retrospective.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--n1PkoHIi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.swarmia.com/static/090bc6f4132597bcf3fec4c4498b849a/00d43/5-step-retrospective.png" alt="Five steps of a retrospective: a personal-experience-centered version using sticky notes" title="Five steps of a retrospective: a personal-experience-centered version using sticky notes" width="880" height="494"&gt;&lt;/a&gt;Five steps of a retrospective: a personal-experience-centered version using sticky notes&lt;/p&gt;

&lt;p&gt;When running a data-driven retrospective, our focus is on the second step, "gather data." You should start the step by first gathering "hard data like events, metrics, features, or stories completed" and then continue to the personal experiences and feelings of the participants.&lt;/p&gt;

&lt;p&gt;However, many teams more or less skip the first part. I've seen dozens of retrospectives where the organizer rushed to ask questions from the group like "What went well? What could be improved? What went badly?" without first thinking about the context.&lt;/p&gt;

&lt;p&gt;To fix the situation, we can add an explicit step that we'll call "present hard data." Trying to gather hard data combined with personal experiences and feelings is a historic relic. Traditionally, retrospectives have been promoted by "agile people" and consultants—external facilitators. Thus, most retrospective advice is written for these kinds of "outsiders."&lt;/p&gt;

&lt;p&gt;An outsider needs to start with "gathering all the data" because they don't often have access to it outside the retrospective. If you're running a modern software organization that employs retrospectives, you have no reason to do a separate data inventory every time. Your "hard data sources" are not likely to change often.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EqyxCjRp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.swarmia.com/static/726ffcbad95b4ebf982ce2371029357b/00d43/6-steps-of-retrospective.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EqyxCjRp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.swarmia.com/static/726ffcbad95b4ebf982ce2371029357b/00d43/6-steps-of-retrospective.png" alt='6-step retrospective with "present hard data" as a separate step' title='6-step retrospective with "present hard data" as a separate step' width="880" height="435"&gt;&lt;/a&gt;6-step retrospective with "present hard data" as a separate step&lt;/p&gt;

&lt;p&gt;When we add the "present hard data" step to our retrospectives, we get four clear benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Better focus:&lt;/strong&gt; We don't need to spend time trying to gather the hard data from the participants. Spending a few minutes going through the most important data is enough. Then, the "gather data" section can focus on getting people to share their personal experiences and feelings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More reliable data:&lt;/strong&gt; Your data quality is better because the data is not based on only what people remember from the past.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better insights:&lt;/strong&gt; Consistent hard data helps the participants generate better insights by reducing personal biases and making it easier to identify common patterns. This ultimately makes it more likely for the team to determine the actual root causes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Continuous evaluation of your hard data:&lt;/strong&gt; The step works as a great "mini-retrospective" on what hard data your team uses to drive its decision-making. If the presented hard data feels irrelevant for the team's continuous improvement, it is a sign that either your hard data or your team may be missing something.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;How to get started with data-driven retrospectives&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;To get started with data-driven retrospectives, take inventory of the "hard data" sources you already have. Many organizations have KPIs, OKRs, or similar to make sure the teams in the organization are prioritizing the right things business-wise. You might also have insights from your version control system like GitHub and issue tracker like Jira. Sales and customer success data is also often valuable.&lt;/p&gt;

&lt;p&gt;Then, think about what data you're currently not collecting but would need to get a holistic picture. For example, &lt;a href="https://www.swarmia.com/blog/well-researched-advice-on-software-team-productivity/#space"&gt;the SPACE framework&lt;/a&gt; is an excellent tool for this. Overall, your goal is to provide the people in the retrospective with an accurate timeline of what happened within the time period you're focusing on (e.g., last iteration or sprint).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It can be hard to correctly remember and jointly discuss past events in a constructive way. Fact-based timelines that visualize a project's events offer a possible solution.&lt;br&gt;
&lt;cite&gt;&lt;br&gt;
~ Bjarnason et al. (&lt;a rel="noopener noreferrer" href="https://ieeexplore.ieee.org/document/6834711"&gt;2014&lt;/a&gt;)&lt;br&gt;
&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You will likely identify hard data that you should have but cannot currently automatically gather. You can compensate for this by making a manual report or making sure you cover it when gathering personal experiences from people.&lt;/p&gt;

&lt;p&gt;For example, many teams don't have very good insights into what kind of work they actually did during the last iteration. You can collect this information by answering these questions: "What did we ship? How much work went into the roadmap/toward our OKRs? How much went to fixing bugs?"&lt;/p&gt;

&lt;p&gt;Another common practice used in many retrospectives is to start every session by building a manual timeline of the last iteration. This reminds people of the key events that may have contributed to the team's productivity and how everyone is feeling.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--c0K7eLk2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.swarmia.com/static/bd1c5de3b4ea07602d096da154e3217d/00d43/retrospective-timeline-metrics-bugs-performance.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--c0K7eLk2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.swarmia.com/static/bd1c5de3b4ea07602d096da154e3217d/00d43/retrospective-timeline-metrics-bugs-performance.png" alt="Example timeline of two quite busy weeks for a team. Can you connect the dots?" title="Example timeline of two quite busy weeks for a team. Can you connect the dots?" width="880" height="280"&gt;&lt;/a&gt;Example timeline of two quite busy weeks for a team. Can you connect the dots?&lt;/p&gt;

&lt;p&gt;It is worth noting that the more automatic you can make your hard data sources, the better. If you rely on manual work in, for example, calculating the ratio between bugs and roadmap work, it is very likely that, at some point, your data will not be up to date.&lt;/p&gt;

&lt;p&gt;Relying on manual work also often leads to the team dropping these metrics when they start feeling busy. This situation is precisely the opposite of what you want to achieve. If anything, you should have a clearer picture of what is essential when you're running low on time.&lt;/p&gt;

&lt;p&gt;To be honest, I haven't personally seen a single team continually do great data-driven retrospectives unless the data and reports were automatically available.&lt;/p&gt;

&lt;h2&gt;
  
  
  A real-life example of holistic hard data
&lt;/h2&gt;

&lt;p&gt;To give a real-life example of a diverse combination of hard data, I will describe the hard data sources we use at Swarmia. We have four different hard data sources: objectives and key results (OKRs), weekly customer reports, sales pipeline, and Swarmia's Work Log.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Objectives and key results (OKRs)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;OKRs include the objectives that we feel are the most important goals for our business right now. Achieving these usually takes multiple months. Each objective has a few (2-6) key results to make them more actionable. We update the progress of our OKRs every Monday.&lt;/p&gt;

&lt;p&gt;Especially in larger organizations, it can be easy to wander off from the things likely to have the most significant business impact. OKRs are an excellent data source for bringing those discussions into retrospectives.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IVs97nmX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.swarmia.com/static/3822250a4618ed5b351e44da6a09b2bf/e4374/objectives-key-results-okrs-engineering-team.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IVs97nmX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.swarmia.com/static/3822250a4618ed5b351e44da6a09b2bf/e4374/objectives-key-results-okrs-engineering-team.png" alt="Example of two engineering team objectives and related key results" title="Example of two engineering team objectives and related key results" width="880" height="587"&gt;&lt;/a&gt;Example of two engineering team objectives and related key results&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Weekly customer report&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Our weekly customer report contains key performance indicators (KPIs) such as monthly recurring revenue (MRR), number of paid seats, user activity, and Slack notification adoption. It provides retrospectives with the necessary context for evaluating if our decisions have been impactful and served our users well.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rRAY3E-7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.swarmia.com/static/12a173afff655d806be78536c9bd3f72/00d43/user-activity-adoption-mau-wau-dau.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rRAY3E-7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.swarmia.com/static/12a173afff655d806be78536c9bd3f72/00d43/user-activity-adoption-mau-wau-dau.png" alt="Example feature adoption graph with monthly, weekly, and daily active users (MAU, WAU, DAU)" title="Example feature adoption graph with monthly, weekly, and daily active users (MAU, WAU, DAU)" width="880" height="538"&gt;&lt;/a&gt;Example feature adoption graph with monthly, weekly, and daily active users (MAU, WAU, DAU)&lt;/p&gt;

&lt;h3&gt;
  
  
  Sales pipeline
&lt;/h3&gt;

&lt;p&gt;We use Hubspot to track our sales pipeline. Seeing the pipeline is helpful even for the engineers. It often provides great context for the features that may be included in the roadmap and is an excellent source of information when discussing the differences between our customers and their needs.&lt;/p&gt;

&lt;p&gt;When your organization becomes bigger, looking at the sales pipeline becomes less valuable. Because of this, our sales team also maintains a "Customer Feature Wishes" page that highlights the most common challenges across the deals.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BwtfoyRD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.swarmia.com/static/c5f367cc52fa99a04184f6ab84919354/00d43/hubspot-sales-pipeline-retrospective.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BwtfoyRD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.swarmia.com/static/c5f367cc52fa99a04184f6ab84919354/00d43/hubspot-sales-pipeline-retrospective.png" alt="An example Hubspot sales pipeline" title="An example Hubspot sales pipeline" width="880" height="547"&gt;&lt;/a&gt;An example Hubspot sales pipeline&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Work Log&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.swarmia.com/product/visibility/"&gt;Work Log&lt;/a&gt; is Swarmia's unique feature that visualizes what has happened within a specific week. It allows us to see at a glance how much of our activities went into the roadmap (e.g., epics or stories) and how much attention we put into individual issues, bugs, and unplanned work. It includes most of the same information as the manually built "timeline" discussed above, except it automatically updates in real-time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jIytmKq1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.swarmia.com/static/a98e28121996b57e42903b3684f85d00/00d43/github-pull-request-jira-issues-work-log.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jIytmKq1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.swarmia.com/static/a98e28121996b57e42903b3684f85d00/00d43/github-pull-request-jira-issues-work-log.png" alt="Swarmia Work Log showing work towards the roadmap, other planned work, bugs, and ad-hoc work" title="Swarmia Work Log showing work towards the roadmap, other planned work, bugs, and ad-hoc work" width="880" height="436"&gt;&lt;/a&gt;Swarmia Work Log showing work towards the roadmap, other planned work, bugs, and ad-hoc work&lt;/p&gt;

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

&lt;p&gt;Hopefully, this blog post inspires you to include enough hard data in your retrospectives. It will allow your teams to create better insights and actions, ultimately leading to more productive teams and happier engineers.&lt;/p&gt;

&lt;p&gt;You can still run retrospectives in which you put the hard data aside and focus on the feelings and personal experiences of the team. Having different kinds of retrospectives will benefit your team and help keep people engaged.&lt;/p&gt;

&lt;p&gt;However, ensure you rely on a wide array of data sources when holding retrospectives on topics in which accurate insights are essential. Leaving hard data out of a retrospective should be a deliberate choice, not an accident.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>devops</category>
      <category>agile</category>
    </item>
    <item>
      <title>4 resources on software team productivity everyone should know</title>
      <dc:creator>Ari Koponen</dc:creator>
      <pubDate>Tue, 05 Oct 2021 07:05:07 +0000</pubDate>
      <link>https://forem.com/apkoponen/4-resources-on-software-team-productivity-everyone-should-know-bn</link>
      <guid>https://forem.com/apkoponen/4-resources-on-software-team-productivity-everyone-should-know-bn</guid>
      <description>&lt;p&gt;&lt;em&gt;Understanding how individual people and productive teams behave is crucial for improving team performance. Often, it can be hard to distinguish ideological advice from insights grounded in research. Here's a list of four reliable resources every software leader should know.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;During my tech career, I've met a multitude of idealistic people who believe following idea X or methodology Y will somehow magically transform a team or an organization. In reality, if you want to make software teams more productive, there are no silver bullets.&lt;/p&gt;

&lt;p&gt;Every software organization is different. People come from different cultures and circumstances. They have varying levels of experience, motivations, and personalities.&lt;/p&gt;

&lt;p&gt;The only real way forward is a relentless process of incremental improvement:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Identify bottlenecks&lt;/li&gt;
&lt;li&gt;Experiment with a possible solution&lt;/li&gt;
&lt;li&gt;Measure&lt;/li&gt;
&lt;li&gt;Analyze&lt;/li&gt;
&lt;li&gt;Repeat&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It’s simple advice but hard to put into practice. One of the biggest challenges in this process of continuous improvement is identifying what you should measure and what kind of behavior you should foster.&lt;/p&gt;

&lt;p&gt;The four resources in this article will give you reliable advice that will help you make meaningful improvements in your organization.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Project Aristotle: Understanding team effectiveness&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;If you've heard that "psychological safety" is the most important factor for creating productive teams, you've heard about &lt;a rel="noopener noreferrer" href="https://rework.withgoogle.com/guides/understanding-team-effectiveness/steps/introduction/"&gt;Project Aristotle&lt;/a&gt;. Project Aristotle is Google's most well-known and quoted research on what makes a team effective.&lt;/p&gt;

&lt;p&gt;The main finding of this research: How teams work together is more important than who is on the team.&lt;/p&gt;

&lt;p&gt;The five key dynamics that affect team effectiveness, in order of priority, are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Psychological safety:&lt;/strong&gt; Team members feel safe to take risks and be vulnerable in front of each other.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependability:&lt;/strong&gt; Team members get things done on time with the expected level of quality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structure and clarity:&lt;/strong&gt; Team members have clear roles, plans, and goals.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Meaning:&lt;/strong&gt; Work is personally important to team members.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Impact:&lt;/strong&gt; Team members think their work matters and creates change.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;The researchers have also provided a list of improvement indicators for each key dynamic. These are signs that your team needs to improve a particular dynamic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Psychological safety:&lt;/strong&gt; Fear of asking for or giving constructive feedback. Hesitance around expressing divergent ideas and asking "silly" questions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependability:&lt;/strong&gt; Team has poor visibility into project priorities or progress. Diffusion of responsibility and no clear owners for tasks or problems.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structure and clarity:&lt;/strong&gt; Lack of clarity about who is responsible for what. Unclear decision-making process, owners, or rationale.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Meaning:&lt;/strong&gt; Work assignments based solely on ability, expertise, and workload. There's little consideration for individual development needs and interests. Lack of regular recognition for achievements or milestones.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Impact:&lt;/strong&gt; Framing work as "treading water." Too many goals, limiting the ability to make meaningful progress.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To understand how your team feels about these dynamics, you can use &lt;a rel="noopener noreferrer" href="https://rework.withgoogle.com/guides/understanding-team-effectiveness/steps/help-teams-determine-their-needs/"&gt;this tool&lt;/a&gt;. It gives you questions that you can discuss together, which will help your team determine their own needs and commit to making improvements.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;DORA: Four key performance metrics&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The DevOps Research and Assessment (DORA) team is a research group that started independently in 2014 and has been part of Google since 2019. They're known for the annual "State of DevOps" reports.&lt;/p&gt;

&lt;p&gt;The DORA team has conducted seven annual global surveys between 2014–2021 that have collected data from more than 32,000 software professionals. From this research, the DORA team has identified four key performance metrics that indicate the performance of a software development team and their ability to provide better business outcomes.&lt;/p&gt;

&lt;p&gt;These four "DORA metrics" are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Deployment frequency:&lt;/strong&gt; How often an organization deploys to production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lead time for change:&lt;/strong&gt; The time it takes to get committed code to run in production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Change failure rate:&lt;/strong&gt; The percentage of production deployments leading to degraded service and remediation (e.g., rollback).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time to restore service:&lt;/strong&gt; How long it takes to fix a failure in production.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first two metrics, deployment frequency and change lead time, measure the velocity of your engineering teams. The second two, mean time to recover and change failure rate, indicate stability. One of the main points of the DORA research is that successful teams can improve their velocity while still achieving great stability.&lt;/p&gt;

&lt;p&gt;The four DORA metrics are the most popular and well-known part of this research. However, it includes many other important insights you should be aware of when applying the metrics. For example, the study lists 24 key capabilities that correlate with team performance.&lt;/p&gt;

&lt;p&gt;The research methodology and results of the State of DevOps are explained in detail in the book &lt;a rel="noopener noreferrer" href="https://www.oreilly.com/library/view/accelerate/9781457191435/"&gt;Accelerate&lt;/a&gt;. It is a must-read for anyone interested in improving engineering team productivity.&lt;/p&gt;

&lt;p&gt;While the DORA metrics are very useful, you should not take them as the one and only measure for improving engineering. The research is more focused on identifying things that correlate with a company's financial performance rather than being a roadmap for improving engineering.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  &lt;strong&gt;SPACE: A framework for developer productivity&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://queue.acm.org/detail.cfm?id=3454124"&gt;SPACE&lt;/a&gt; is a framework for developer productivity. It looks at developer productivity from the perspective of different organizational levels (individuals, teams &amp;amp; groups, and systems) and dimensions (&lt;strong&gt;S&lt;/strong&gt;atisfaction &amp;amp; Well-being, &lt;strong&gt;P&lt;/strong&gt;erformance, &lt;strong&gt;A&lt;/strong&gt;ctivity, &lt;strong&gt;C&lt;/strong&gt;ommunication &amp;amp; Collaboration, and &lt;strong&gt;E&lt;/strong&gt;fficiency &amp;amp; Flow).&lt;/p&gt;

&lt;p&gt;A member of the DORA team and author of the book Accelerate, Nicole Forsgren, led this study. The goal of SPACE is to give a more complete and nuanced picture of developer productivity beyond the specific metrics included in DORA.&lt;/p&gt;

&lt;p&gt;The SPACE framework aims to overcome the flaws of earlier attempts to measure developer productivity. These "myths and misconceptions about developer productivity" include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Productivity is all about developer activity.&lt;/li&gt;
&lt;li&gt;Productivity is only about individual performance.&lt;/li&gt;
&lt;li&gt;One productivity metric can tell us everything.&lt;/li&gt;
&lt;li&gt;Productivity measures are useful only for managers.&lt;/li&gt;
&lt;li&gt;Productivity is only about engineering systems and developer tools.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To counteract these myths, the SPACE framework "provides a way to think rationally about productivity in a much bigger space." It lists five different dimensions of developer productivity you should measure instead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Satisfaction and well-being:&lt;/strong&gt; "Satisfaction is how fulfilled developers feel with their work, team, tools, or culture; well-being is how healthy and happy they are, and how their work impacts it." Example metrics include employee satisfaction, developer efficacy, and burnout.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance:&lt;/strong&gt; Trying to measure performance through outputs often leads to suboptimal business outcomes. Therefore, for software development, performance is best evaluated as outcomes instead of output. Example metrics include reliability, absence of bugs, and customer adoption.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Activity:&lt;/strong&gt; Countable activities that happen when performing work, such as design documents, commits, and incident mitigation, are almost impossible to measure comprehensively. Therefore, you should never use them in isolation and instead always balance them with metrics from other dimensions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Communication and collaboration:&lt;/strong&gt; How well do teams communicate and work together? Example metrics include discoverability of documentation, quality of reviews, and onboarding times for new team members.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Efficiency and flow:&lt;/strong&gt; These "capture the ability to complete work or make progress on it with minimal interruptions or delays." Example metrics include the number of handoffs in a process, perceived ability to stay in flow, and time measures through the system (total time, value-added time, and wait time).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a fuller picture of what is happening in your organization, the SPACE framework suggests that you have metrics for these dimensions on three different levels of your organization:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Individual&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Team or group:&lt;/strong&gt; People who work together.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;System:&lt;/strong&gt; End-to-end work through a system, e.g., the development pipeline of your organization from design to production.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The SPACE framework authors provide an example table with metrics for each dimension and organization level. It gives you a concrete idea of what metrics you could have in your organization. The metrics proposed in the example are quite GitHub-centric. This is natural as the SPACE framework researchers mostly work at GitHub and Microsoft (owner of GitHub).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F87shfd2wsjljzp4ba01e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F87shfd2wsjljzp4ba01e.png" alt="One example of SPACE Framework metrics you can use based on the original research"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The example table brings us to the biggest strength and challenge of the SPACE framework: You will have to make it your own. There are multiple options for what you could use as a metric in your organization, and the metrics you choose depend on what fits your culture and the data you have available.&lt;/p&gt;

&lt;p&gt;There are three things to keep in mind with SPACE. First, you should include metrics from at least three dimensions. Second, you should include metrics from all levels of the organization. Third, at least one of the metrics should include perceptual measures such as survey data. Including perceptions about people's lived experiences can often provide more complete and accurate information than metrics gathered from system behavior alone.&lt;/p&gt;

&lt;p&gt;When the above recommendations are fulfilled, you will be able to construct a more complete picture of productivity.&lt;/p&gt;

&lt;p&gt;It is also likely that having multiple metrics will create metrics in tension. This is by design. Having a more balanced view should help you make smarter decisions and tradeoffs among your teams and the whole organization. It will also make sure you're not optimizing a single metric to the detriment of the entire system.&lt;/p&gt;

&lt;p&gt;If you want to start applying the SPACE framework to your organization, you can take a look at our free editable canvas (&lt;a rel="noopener noreferrer" href="https://www.swarmia.com/downloads/free-space-framework-canvas-swarmia.pdf"&gt;PDF&lt;/a&gt;, &lt;a rel="noopener noreferrer" href="https://docs.google.com/document/d/1RIjtPEx6M8MHXkTlkigE11XyAV_AUlubF6VMrALif24/edit?usp=sharing"&gt;Google Docs&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Retrospectives: We may be lying to ourselves&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Software organizations often use retrospectives as a mechanism for continuous improvement. Many people have inherited this practice from some vein of agile thinking. Some hold them because they've personally had some good experiences with them, others because they feel that "they are a best practice."&lt;/p&gt;

&lt;p&gt;Retrospectives can be an excellent tool for continuous improvement, and you should probably use them in some form if you're already not doing so. However, know that retrospectives have their limits. &lt;a rel="noopener noreferrer" href="https://link.springer.com/article/10.1007/s10664-016-9464-2"&gt;This study on retrospectives&lt;/a&gt; does a great job of highlighting some of the common challenges that you're likely to encounter.&lt;/p&gt;

&lt;p&gt;First, team-level retrospectives are often "weak at recognizing issues and successes related to the process areas that are external to the concerns of the teams." Software teams often talk—or complain—about organization-level issues in retrospectives: collaboration with sales, product managers, general management, etc. However, this discussion rarely results in any tangible changes in the broader organization.&lt;/p&gt;

&lt;p&gt;If you want to hold effective retrospectives and drive change, mainly focus on topics that the team can control or directly affect. Whenever the team regularly discusses subjects outside of their direct influence, make sure the feedback is addressed on the level of the organization, where real change can be created.&lt;/p&gt;

&lt;p&gt;Alternatively, your team's understanding of the surrounding business and organization's goals may be incorrect. In these cases, aim to provide the team with more context.&lt;/p&gt;

&lt;p&gt;Second, software team members have biases. For example, "the development teams tend to find people factors as more positive and avoid stating themselves as a target for improvement." The study also lists situations where the way the team felt did not correspond with the real-world evidence on what had happened during the past few weeks.&lt;/p&gt;

&lt;p&gt;To counteract biases, don’t rely purely on the personal experiences of the team members. Instead, include "fact-based evidence to the retrospective in order to base the analysis on a more objective picture of the real situation." &lt;a rel="noopener noreferrer" href="https://www.sciencedirect.com/science/article/abs/pii/S095058490800030X?via%3Dihub"&gt;This study&lt;/a&gt; proposes the use of a visual timeline of project events collected from various sources. There are many ways you could do this with your team. You can check out &lt;a rel="noopener noreferrer" href="https://www.swarmia.com/product/visibility/"&gt;Swarmia's work log&lt;/a&gt; as an example.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxg2ajijejpkgvpjh0rik.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxg2ajijejpkgvpjh0rik.png" alt="Swarmia's Work Log showing a fairly successful week for the team, useful for retrospective discussions"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Godspeed!&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;I believe understanding the principles and mechanisms above will help you improve your software team's performance. This research also drives our product development at Swarmia. We've seen teams following this advice cut their cycle time by 40% within two months.&lt;/p&gt;

&lt;p&gt;Our product condenses much of the above information within a single tool that is designed to give you easy access to all the key metrics and help you continuously improve. If you're interested in seeing what it looks like and how it might help your team, &lt;a href="https://www.swarmia.com/" rel="noopener noreferrer"&gt;learn more&lt;/a&gt; or &lt;a href="https://www.swarmia.com/demorequest" rel="noopener noreferrer"&gt;get started&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What resources do you yourself find reliable?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>career</category>
      <category>discuss</category>
      <category>programming</category>
    </item>
    <item>
      <title>Busting the 10x software engineer myth</title>
      <dc:creator>Ari Koponen</dc:creator>
      <pubDate>Tue, 21 Sep 2021 12:31:42 +0000</pubDate>
      <link>https://forem.com/apkoponen/busting-the-10x-software-engineer-myth-2gom</link>
      <guid>https://forem.com/apkoponen/busting-the-10x-software-engineer-myth-2gom</guid>
      <description>&lt;p&gt;&lt;em&gt;Leaders often feel proud of their high performers. In reality, having "10x engineers" in your software organization may be a sign of an organization that is set out to fail in the long run.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As an engineering leader, it is very tempting to focus on the few individuals performing well in your organization and build your success around these people. After all, they are – seemingly – the smartest and most reliable people who create the best solution in the shortest amount of time.&lt;/p&gt;

&lt;p&gt;Everyone working in software engineering has seen people who seem to get much more done than others. If not "10x," then at least “5x” or “3x engineers”. When looking at just everyday information, it seems reasonable that some engineers are just inherently more productive than others.&lt;/p&gt;

&lt;p&gt;However, the science behind "10x engineers" is not very solid. The most cited studies on the subject were done decades ago in a very different environment and – more importantly – with a fairly small sample size. One of &lt;a rel="noopener noreferrer" href="https://ieeexplore.ieee.org/document/8804291"&gt;the most interesting recent studies&lt;/a&gt; comes to a very different conclusion. In a controlled environment, the variation between most software engineers is much more modest. There is also a significant variation in how the same person performs across multiple tasks. A person who outperforms others in some task may be average in another. “In summary, while programmers are better and/or faster than others, the importance and scale has been greatly exaggerated.”&lt;/p&gt;

&lt;p&gt;The myth of 10x engineers is a great example of confirmation bias at work.&lt;/p&gt;

&lt;h2&gt;
  
  
  What explains productivity differences, if not mere individual talent?
&lt;/h2&gt;

&lt;p&gt;When we take into account the surrounding environment – the organization as a whole – it is easy to spot multiple factors that affect performance in addition to individual talent. Do these two engineers have the same experience with the technology at hand? How familiar are they with the system they're working on? Do they have the same ability to make decisions on the go? How easily can they get help?&lt;/p&gt;

&lt;p&gt;Now, take a look at your “wow that person gets things done” -experiences with these factors in mind. They often become quite mundane. People love heroics and relish the possibility of working with “the best.” It is a comforting reality. Much nicer than being honest about the challenges and realities in the organization. The things that hinder performance. Things that often feel very hard – if not impossible – to change.&lt;/p&gt;

&lt;p&gt;As a real-life example, I once worked with a company where the company’s leadership considered a specific developer infinitely better than the others. The company's CEO would walk into the space where the developers worked and loudly comment – even to outsiders – “sometimes it feels like X is the only developer who gets anything done.” The developer was visibly proud of the feedback. Others were left obviously demotivated.&lt;/p&gt;

&lt;p&gt;No one talked about the fact that this particular developer had been developing the same system for the past 15 years, the first five almost alone. He relished being “the best." Wrote hard-to-understand procedural code to 5000-line files. Did not actively share knowledge with his peers. Over the years, some capable developers had joined the company. Most of them decided to move on. The ones left were happy to coast along and leave all the heroics to the “10x engineer.” As a result, the company had great difficulties in trying to scale its development efforts.&lt;/p&gt;

&lt;p&gt;This is, of course, a very extreme example but illustrates the problem well. Having a tiny percentage of high-performing engineers is often a symptom of bigger organizational issues like siloing or imbalance in power and responsibility. It is not just the natural order of things that is impossible for you to fight against.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building a better strategy for achieving business success
&lt;/h2&gt;

&lt;p&gt;From the perspective of the business's overall success, finding a few 10x unicorn developers is not what will make it or break it for you in the long run. The real key to success is to improve the average performance of everyone in the company. This is – statistically speaking – a more viable strategy.&lt;/p&gt;

&lt;p&gt;The likelihood for those unicorns staying in your company for 5 or 10 years is also very low – unless you have a somehow extraordinary company. Even if they stay, they’ll be changing teams – and every time, they leave the teams built around their behavior scrambling. Again, hurting your overall ability to execute your business goals.&lt;/p&gt;

&lt;p&gt;Instead, improving your organization to make more people productive will reap the greatest benefits. It will allow you to hire more people who can succeed in your engineering organization. The broader you can turn the pool of people you feel “could do great work at our company,” the better your odds for scaling with success. This realization should make us more holistic. Turn our attention from individuals to groups of people: teams, departments, and the whole organization.&lt;/p&gt;

&lt;p&gt;This is good news. There is plenty of good research on software team performance. If your team is struggling with too many bugs in production or you have issues with shipping changes quickly take a look at the &lt;a rel="noopener noreferrer" href="https://cloud.google.com/blog/products/devops-sre/using-the-four-keys-to-measure-your-devops-performance"&gt;DORA metrics&lt;/a&gt;. If your team does not collaborate well together or team members are not taking ownership, learn &lt;a rel="noopener noreferrer" href="https://rework.withgoogle.com/guides/understanding-team-effectiveness/steps/foster-psychological-safety/"&gt;how to foster psychological safety&lt;/a&gt;. To understand the full landscape of developer productivity, check out the &lt;a rel="noopener noreferrer" href="https://queue.acm.org/detail.cfm?id=3454124"&gt;SPACE framework&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Whatever you do, the key to success is to understand your team's bottlenecks and take action together with the team to improve the situation. Some things need one-off changes. Other's require habits. All team habits should be documented into a common &lt;a href="https://www.swarmia.com/product/ways-of-working/"&gt;working agreement that everyone is aware of and follows&lt;/a&gt;. Useful habits compound over time and create productive teams and organizations.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;TL;DR;&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The best strategy for most companies is not a relentless focus on trying to hire “the best” in hopes of finding 10x unicorn engineers. Rather, it is to make sure that the normal, capable engineers you can hire and retain perform their best. The organizations that can turn ordinary teams into high-performing ones will be the winners – and the engineers in them too.&lt;/p&gt;

&lt;p&gt;--&lt;/p&gt;

&lt;p&gt;(Cover image: A 10x engineer at work in Chernobyl. The Hollywood Archive / Avalon)&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>programming</category>
      <category>career</category>
      <category>leadership</category>
    </item>
    <item>
      <title>Story of Pixel: How we made an off-the-shelf design system our own</title>
      <dc:creator>Ari Koponen</dc:creator>
      <pubDate>Fri, 23 Jul 2021 10:52:23 +0000</pubDate>
      <link>https://forem.com/supermetrics/story-of-pixel-how-we-made-an-off-the-shelf-design-system-our-own-3fnj</link>
      <guid>https://forem.com/supermetrics/story-of-pixel-how-we-made-an-off-the-shelf-design-system-our-own-3fnj</guid>
      <description>&lt;p&gt;&lt;em&gt;How we used a ready-made design system as the basis of our design system, Pixel, but made it our own.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;Supermetrics has been doubling in size for the past five years. In the beginning, the product was just a single sidebar within Google docs, and most code was on the backend. UI possibilities and requirements were limited. Today, Supermetrics has multiple applications and around ten front-end developers working across five teams. For the company, great UX is among the most critical competitive advantages.&lt;/p&gt;

&lt;p&gt;The front-end applications have grown organically over the years. Because of the history, many UI components were not reusable. Styles were copy-pasted between applications. Despite SCSS variables and naming conventions, there were consistency issues with colors, spacing, and font sizes.&lt;/p&gt;

&lt;p&gt;Building the new design system was one of my most exciting projects while I was working at Supermetrics. I had joined the company last October, and one part of my job description was "redesigning our Team site with our Head of Design." The main business drivers for the redesign were that development was slow, the UI was inconsistent, and it was hard to support the new most important user flows. Previously, the UI was mainly used for license and team management and was structured around those needs. Now, users have more complex needs like configuring and monitoring data transfers. This requires a very different approach to the UI.&lt;/p&gt;

&lt;p&gt;Redesigning the Team site is a significant undertaking. It is Supermetrics' biggest custom UI written in React, TypeScript, and SCSS with hundreds of components and almost 100K lines of code. Redesigning it would also lead to updating the UIs of other applications to keep a consistent user experience across the product suite.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Goal
&lt;/h2&gt;

&lt;p&gt;We started discussing with our Head of Design Ivana Pesic and some of the front-end developers what would be the best way for us to achieve our business goals: supporting the new flows, consistency across all our apps, and making development faster. One of the most promising ideas was to build a design system. This would allow us to have a central repository of UI components and assets together with accompanying guidelines and documentation.&lt;/p&gt;

&lt;p&gt;Our team was already somewhat familiar with the concept of design systems but wanted to make sure it was a worthwhile undertaking. We read about other companies' experiences and resources like Brad Frost’s &lt;a href="https://atomicdesign.bradfrost.com/" rel="noopener noreferrer"&gt;Atomic Design&lt;/a&gt; book to improve our understanding. These gave us further confidence that a design system could help us sustainably reach our goals. &lt;/p&gt;

&lt;p&gt;We set out to create a design system of our own with four goals in mind:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consistently great UI.&lt;/strong&gt; Make sure all teams could create UIs that feel like they belong together.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shared vocabulary.&lt;/strong&gt; Improve the communication between designers, developers, and other stakeholders in the company around designing and building UIs with great UX.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Educating people within the organization.&lt;/strong&gt; Raise the bar of our UI/UX design across the whole organization by giving people tools that help them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Improve speed of development.&lt;/strong&gt; Stop reinventing the wheel and copy-pasting when it is not useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing the approach
&lt;/h2&gt;

&lt;p&gt;Once we knew why we were building a design system, it was time to decide how we'd do it. Should we build our design system from scratch or use an off-the-shelf solution? Building from scratch gives you more control and makes the design system reflect your company brand better. However, it is very time-consuming, and there are no real guarantees that the result is any better than using an off-the-shelf one. To make a decision, we needed to know what we needed.&lt;/p&gt;

&lt;p&gt;To gather design requirements, we:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Did a component inventory of all the components we'd need. &lt;/li&gt;
&lt;li&gt;Refreshed our brand book and style guide.&lt;/li&gt;
&lt;li&gt;Made some designs to have an idea of the overall design style.&lt;/li&gt;
&lt;li&gt;Researched many of the available options to have a general idea of what ready-made solutions were available.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the technical side, we listed out the technical things we'd want to have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Strictly-typed TypeScript. For great auto-complete and type-safety.&lt;/li&gt;
&lt;li&gt;Maintainable CSS. For us, this meant: style-colocation, avoiding global scope, dead code elimination, and enforcing the design system styles, while still allowing one-off overrides.&lt;/li&gt;
&lt;li&gt;Themeability. We knew we wanted to change the colors and fonts of any ready-made solution and themeability means that our changes are much less likely to break.&lt;/li&gt;
&lt;li&gt;Tree-shakeability / ES modules. We knew all applications wouldn't use the whole design system, and we wanted the JavaScript bundle sizes to stay small for a great user experience.&lt;/li&gt;
&lt;li&gt;Copy-pasteable component code. If we would need to modify some of the components heavily, we wanted to make sure we could use the original component as the basis.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these requirements in mind, we researched if any of the ready-made options would fit. In this process, UXPin's Adele was very helpful. We found multiple promising options and dug a little deeper into them. At first, none of them seemed to fit our requirements, and we seriously considered building everything from scratch or using a UI component library like Chakra UI and adding the rest ourselves. &lt;/p&gt;

&lt;p&gt;In the end, after much discussion and evaluation, we concluded Zendesk's Garden was the best fit for us in terms of technologies, default styles, and themeability. I sent the Garden documentation to some of our front-end developers for review, and all concluded that it looked good.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making the design system our own
&lt;/h2&gt;

&lt;p&gt;Although Zendesk's Garden matched our needs, we did not want just to copy their styles and call it a day. We gave the design system the name "Pixel" after our cute dog-mascot. We created a custom theme with our fonts, colors, shadows, and spacings. We also added some Supermetrics feel to the designs with small details like fully rounded buttons, SVG graphics, and hand-selected icons.&lt;/p&gt;

&lt;p&gt;Also, on the technical side, we did not want our apps to depend directly on Zendesk's packages that we had no control over. We created a separate, privately hosted NPM package for Pixel, where we individually import and export the components that we want to use from Zendesk's packages. In this way, we can get all bug fixes and new features from Zendesk's Garden simply by updating our dependencies while still distributing our customizations to our own apps in a controlled way. We can also replace any part of the design system without affecting the consuming apps. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyu1qmlihbc1t67tqlhes.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyu1qmlihbc1t67tqlhes.png" alt="Pixel in action in our license checkout flow." width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Pixel in action in our license checkout flow.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons learned from building on top of a ready-made design system
&lt;/h2&gt;

&lt;p&gt;As we expected, there are pros and cons to using a ready-made design system.&lt;/p&gt;

&lt;h3&gt;
  
  
  The pros
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Fast time to use.&lt;/strong&gt; The design system was used in three apps in less than six months from the start of the project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A great technical foundation.&lt;/strong&gt; The design system we chose had a ready-made Storybook and tests that we can use even when making changes to the components.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;More concrete discussion.&lt;/strong&gt; Our design discussions often start with the capabilities of the design system we're building on top of. This frames the discussion, allows us to find practical solutions faster, and helps us iterate in smaller steps.&lt;/p&gt;

&lt;h3&gt;
  
  
  The cons
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Unscheduled fundamental discussions.&lt;/strong&gt; With a ready-made design system, you skip important fundamental discussions. Our organization thinks differently about design than Zendesk. The differences often manifest themselves when we're designing and building a feature. Sometimes we've needed to start unexpectedly asking very fundamental questions about things like buttons and links or animations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dirty overrides to the ready-made components.&lt;/strong&gt; We've aligned our designs to the limits of the ready-made design system as much as is feasible. Still, we have needed style overrides for, e.g., button variants. These overrides always feel a little bit hacky and may break at some point when the ready-made design system breaks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solutions
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Accepting the trade-off we've made.&lt;/strong&gt; We knew we were taking UI/UX and technical design debt when we chose to speed up things with a ready-made solution. This debt has to be paid back, and sometimes this happens at inconvenient times. Mentally accepting this and being prepared when working on new features helps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using a separate NPM package.&lt;/strong&gt; We have mitigated most of the downsides of depending on an existing design system by our own private NPM Package. For example, we have already modified the Garden typography components heavily. We were able to copy-paste the original components to our package and modify them to our needs without making it feel hacky for consuming applications. We've also added a complete set of layout components inspired by SEEK's Braid design system, which we've built from scratch. For the consuming applications, there is no difference.&lt;/p&gt;

&lt;h2&gt;
  
  
  Future
&lt;/h2&gt;

&lt;p&gt;In total, Pixel is now used in four different applications. The redesign of the Team site with Pixel has also begun, which makes the team super excited. The redesign will require many new UI components, some of which will belong to the design system and some which are application-specific.&lt;/p&gt;

&lt;p&gt;In its current state, Pixel is still more like a component library than a full-blown design system. The documentation around the thinking on design philosophy and copywriting guidelines still need to be polished. The work has only begun, but now there is a great place to iterate and improve on.&lt;/p&gt;




&lt;p&gt;What is it like to be an engineer at Supermetrics? 👉 &lt;a href="https://supermetrics.com/careers/engineering" rel="noopener noreferrer"&gt;supermetrics.com/careers/engineering&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>design</category>
      <category>react</category>
    </item>
    <item>
      <title>How to Disable Server-Side Rendering (SSR) in Next.js</title>
      <dc:creator>Ari Koponen</dc:creator>
      <pubDate>Wed, 23 Dec 2020 08:05:49 +0000</pubDate>
      <link>https://forem.com/apkoponen/how-to-disable-server-side-rendering-ssr-in-next-js-1563</link>
      <guid>https://forem.com/apkoponen/how-to-disable-server-side-rendering-ssr-in-next-js-1563</guid>
      <description>&lt;p&gt;&lt;em&gt;Learn how to disable SSR in Next.js and use it to replace Create React App or any custom tooling.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Next.js is my absolute favorite tool for developing React applications. It offers you a best-in-class developer experience. It also has a ton of useful features for being more productive and optimizing your apps in production:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Static &amp;amp; server rendering &lt;/li&gt;
&lt;li&gt;TypeScript support&lt;/li&gt;
&lt;li&gt;Multiple entry points&lt;/li&gt;
&lt;li&gt;Bundle-splitting and tree-shaking&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, in the past, I did not want to use it for all my React apps. Next.js does not work without server-side rendering (SSR) by default. I preferred using a non-SSR solution like Create React App, when my app did not require SSR, because SSR had caused me so many unnecessary problems. &lt;/p&gt;

&lt;p&gt;Then, one day Tanner Linsley, the author of React Query, posted on Twitter that he was using Next.js without SSR as a replacement for Create React App:&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1303818811322195969-826" src="https://platform.twitter.com/embed/Tweet.html?id=1303818811322195969"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1303818811322195969-826');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1303818811322195969&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;I was stoked! After a little bit of research, I was able to do this myself. 🤯 &lt;/p&gt;

&lt;h1&gt;
  
  
  Why Disable SSR in Next.js?
&lt;/h1&gt;

&lt;p&gt;SSR for React apps is necessary in a few cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The app's content needs to rank high on search results (SEO).&lt;/li&gt;
&lt;li&gt;You need social-media previews (Facebook, Twitter, Slack, etc.).&lt;/li&gt;
&lt;li&gt;You need the additional speed optimizations it can provide for your users.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, using SSR has multiple trade-offs and challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You need a complicated hosting environment.&lt;/strong&gt; You cannot just upload your app to a server or a CDN. You need Node.js servers doing the server-side rendering. This adds complexity and also costs more.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You need to make sure your code works both on the browser and on the server (Node.js).&lt;/strong&gt; This makes debugging harder and restricts you in some cases. For example, you cannot use localStorage to store authorization information, but you need to pass it in a cookie and use a cookie library that works on the server and the browser. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1230449374091784194-847" src="https://platform.twitter.com/embed/Tweet.html?id=1230449374091784194"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1230449374091784194-847');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1230449374091784194&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It affects your application architecture.&lt;/strong&gt; For example, server-side rendering needs to be done in a single render, so you need to fetch all the data for the page in a single location (like &lt;code&gt;getInitialProps&lt;/code&gt;). This requirement complicates data fetching with libraries like Redux or React Query and often leads to duplicate code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you don't need SSR, these trade-offs are not worth it. Basically, you should consider disabling SSR for all apps where the UI is behind a login.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Heuristic: If your app is behind a login, you likely should disable SSR.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  How can you disable SSR in Next.js?
&lt;/h1&gt;

&lt;p&gt;Let's go through the steps for disabling SSR for a fresh Next.js application (created with &lt;code&gt;npx create-next-app&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Rewrite All Requests to &lt;code&gt;pages/index.js&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Next.js supports &lt;a href="https://vercel.com/docs/configuration#project/redirects" rel="noopener noreferrer"&gt;adding redirects&lt;/a&gt;. Create a file named &lt;code&gt;next.config.js&lt;/code&gt; to the root of your project. Add the following configuration there:&lt;/p&gt;

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

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;serverless&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;rewrites&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="c1"&gt;// Rewrite everything to `pages/index`&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/:any*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;These redirects work only in the development environment. In production, you need to have a proxy server like NGINX or use your hosting platform's capabilities (e.g. &lt;a href="https://docs.netlify.com/routing/redirects/" rel="noopener noreferrer"&gt;Netlify's redirects&lt;/a&gt;) for doing these redirects. &lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Disable SSR for Page Content
&lt;/h2&gt;

&lt;p&gt;To disable SSR for page content, we need to add the following code to &lt;code&gt;pages/_app.js&lt;/code&gt;:&lt;/p&gt;

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

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../styles/globals.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;SafeHydrate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;suppressHydrationWarning&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pageProps&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SafeHydrate&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Component&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;pageProps&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;SafeHydrate&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;MyApp&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;In the code above, we wrap our page content to a component called &lt;code&gt;SafeHydrate&lt;/code&gt; that allows us to prevent the page content from rendering on the server. Let's go through what is happening in the code above. &lt;/p&gt;

&lt;p&gt;With Next.js you can check if we're on the server by checking if the &lt;code&gt;window&lt;/code&gt; object is &lt;code&gt;undefined&lt;/code&gt;.&lt;/p&gt;

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

&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;undefined&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="c1"&gt;// This code will only execute on the server &lt;/span&gt;
  &lt;span class="c1"&gt;// and not in the browser&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;However, we cannot just wrap our code into this &lt;code&gt;if&lt;/code&gt;-statement directly. If you try it, you'll notice that React will produce an annoying hydration mismatch warning in the console: &lt;code&gt;Warning: Expected server HTML to contain a matching &amp;lt;div&amp;gt; in &amp;lt;div&amp;gt;.&lt;/code&gt; This happens, if the server HTML is different from what the browser renders. &lt;/p&gt;

&lt;p&gt;In our case, it is safe to ignore this warning. To keep things tidy, we want to completely hide the warning from the console. This can be done by rendering a div with the prop &lt;code&gt;suppressHydrationWarning&lt;/code&gt;. For better readability, we create a separate &lt;code&gt;SafeHydrate&lt;/code&gt; component for this and wrap our page component into it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Check that Everything Works with &lt;code&gt;npm run dev&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Now, run &lt;code&gt;npm run dev&lt;/code&gt; in your terminal. After the server is running at &lt;a href="http://localhost:3000/" rel="noopener noreferrer"&gt;http://localhost:3000/&lt;/a&gt; you should able to go to any URL (like &lt;a href="http://localhost:3000/some/random/path" rel="noopener noreferrer"&gt;http://localhost:3000/some/random/path&lt;/a&gt;) and see the content of index.js there.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftw9l5iacvlko87cpgxhd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftw9l5iacvlko87cpgxhd.png" alt="Screenshot 2020-12-10 at 12.20.29" width="800" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Success! 🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Build production bundles with &lt;code&gt;next export&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;We want to deploy our app as a static bundle that can be served without a Node.js server. For this, Next.js offers the command &lt;code&gt;next export&lt;/code&gt;. It will create a static version of your app in the &lt;code&gt;out&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;To use the command, update the "build" script in your &lt;code&gt;package.json&lt;/code&gt; like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"next build &amp;amp;&amp;amp; next export"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


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

&lt;/div&gt;

&lt;p&gt;Now, run &lt;code&gt;npm run build&lt;/code&gt;. When you see the message &lt;code&gt;Export successful&lt;/code&gt;, congrats! You now have a working static Next.js app in the &lt;code&gt;out&lt;/code&gt; directory. 🎉&lt;/p&gt;

&lt;p&gt;You can check the whole example app from &lt;a href="https://github.com/apkoponen/next-js-disable-ssr" rel="noopener noreferrer"&gt;this Github repository&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Notes on Routing and other Advanced features
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Routing
&lt;/h2&gt;

&lt;p&gt;Next.js does not support dynamic routing if you don't have a server running. You need a router like &lt;code&gt;react-router&lt;/code&gt;. The setup is the same as with other tools like Create React App. &lt;/p&gt;

&lt;h2&gt;
  
  
  Updating the &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; and Other &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; Tags
&lt;/h2&gt;

&lt;p&gt;You don't need to add something like &lt;code&gt;react-helmet&lt;/code&gt; for updating the &lt;code&gt;head&lt;/code&gt;, Next.js &lt;code&gt;&amp;lt;Head /&amp;gt;&lt;/code&gt; component will work. &lt;/p&gt;

&lt;h2&gt;
  
  
  Having Multiple Separate Pages
&lt;/h2&gt;

&lt;p&gt;If you want, you can still use Next.js pages to have multiple different pages as separate entry points for your app. This will make your bundles per route smaller and speed up your development environment because only a part of the app will be built when you make changes.&lt;/p&gt;

&lt;p&gt;For example, if you have a page &lt;code&gt;/accounts&lt;/code&gt; you can create a file &lt;code&gt;pages/account.js&lt;/code&gt; and add a corresponding rewrite:&lt;/p&gt;

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

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;serverless&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;rewrites&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="c1"&gt;// Rewrite everything under `/account/ to `pages/account`&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/account/:any*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/account&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="c1"&gt;// Rewrite everything else to `pages/index`&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/:any*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="s2"&gt;```

# How's this different from using Next.js `&lt;/span&gt;&lt;span class="nx"&gt;getStaticProps&lt;/span&gt;&lt;span class="s2"&gt;` with `&lt;/span&gt;&lt;span class="nx"&gt;getStaticPaths&lt;/span&gt;&lt;span class="s2"&gt;`?

Using `&lt;/span&gt;&lt;span class="nx"&gt;getStaticProps&lt;/span&gt;&lt;span class="s2"&gt;` with `&lt;/span&gt;&lt;span class="nx"&gt;getStaticPaths&lt;/span&gt;&lt;span class="s2"&gt;` allows you to do Static Site Generation (SSG). This means that all the pages in your app are generated as individual `&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`-files when you run `&lt;/span&gt;&lt;span class="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="s2"&gt;`. 

SSG is awesome, but has a single big limitation: **You need to know all the paths your app has ahead of time**. This is not possible with many apps that have tons of user-specific paths like `&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;my&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;payments&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;123121521241&lt;/span&gt;&lt;span class="s2"&gt;`. 

With the approach described in this article, you can use a dynamic router like `&lt;/span&gt;&lt;span class="nx"&gt;react&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="s2"&gt;` with Next.js just like you would with Create React App or any traditional single-page app.

# Additional Resources:

- [This wonderful gist by @tannerlinsley](https://gist.github.com/tannerlinsley/65ac1f0175d79d19762cf06650707830)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>react</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How to Create a ButtonGroup Component with React and Tailwind CSS?</title>
      <dc:creator>Ari Koponen</dc:creator>
      <pubDate>Sat, 16 May 2020 12:30:19 +0000</pubDate>
      <link>https://forem.com/apkoponen/how-to-create-a-buttongroup-component-with-react-and-tailwind-css-1obo</link>
      <guid>https://forem.com/apkoponen/how-to-create-a-buttongroup-component-with-react-and-tailwind-css-1obo</guid>
      <description>&lt;p&gt;Button groups are a great example of a situation where you need to alter the styles of a child component based on its position within its parent. Doing this in React can sometimes be tricky, and styling components with Tailwind CSS adds its flavor to the challenge.&lt;/p&gt;

&lt;p&gt;Let's recreate this &lt;a href="https://tailwindui.com/components/application-ui/elements/button-groups"&gt;Tailwind UI button group&lt;/a&gt;  as a React component:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CPYQX_j5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/km41z1eru4393grgfkhk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CPYQX_j5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/km41z1eru4393grgfkhk.png" alt="Tailwind UI Button Group"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The JSX for this component might be something like this:&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ButtonGroup&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Years&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Months&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Years&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ButtonGroup&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are several different ways we could style these components with Tailwind in React. I will go through four different approaches that each teach you something new about handling parent-child relationships and styles in React.&lt;/p&gt;

&lt;h2&gt;
  
  
  Method 1: PostCSS @apply
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://codesandbox.io/s/method-1-postcss-apply-4edyo?file=/src/App.js"&gt;View in Codesandbox&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This method utilizes Tailwind's &lt;a href="https://tailwindcss.com/docs/extracting-components/#extracting-css-components-with-apply"&gt;@apply directive&lt;/a&gt;. If you've used Tailwind outside of React before, this is likely very familiar to you.&lt;/p&gt;

&lt;p&gt;With @apply, we can write normal CSS selectors and override our child styles. To do this, we add a &lt;code&gt;.btn-group&lt;/code&gt; class to our &lt;code&gt;&amp;lt;ButtonGroup /&amp;gt;&lt;/code&gt; component and &lt;code&gt;.btn&lt;/code&gt; class to our &lt;code&gt;&amp;lt;Button /&amp;gt;&lt;/code&gt;. This is very similar to how &lt;a href="https://getbootstrap.com/docs/4.4/components/button-group/"&gt;Bootstrap&lt;/a&gt; handles button group styles.&lt;/p&gt;

&lt;p&gt;Our CSS looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;components&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nc"&gt;.btn&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;@apply&lt;/span&gt; &lt;span class="err"&gt;relative&lt;/span&gt; &lt;span class="err"&gt;inline-flex&lt;/span&gt; &lt;span class="err"&gt;items-center&lt;/span&gt; &lt;span class="err"&gt;px-2&lt;/span&gt; &lt;span class="err"&gt;py-2&lt;/span&gt; &lt;span class="err"&gt;border&lt;/span&gt; &lt;span class="err"&gt;border-gray-300&lt;/span&gt; &lt;span class="err"&gt;bg-white&lt;/span&gt; &lt;span class="err"&gt;text-sm&lt;/span&gt; &lt;span class="err"&gt;leading-5&lt;/span&gt; &lt;span class="err"&gt;font-medium&lt;/span&gt; &lt;span class="err"&gt;text-gray-500&lt;/span&gt; &lt;span class="err"&gt;transition&lt;/span&gt; &lt;span class="err"&gt;ease-in-out&lt;/span&gt; &lt;span class="err"&gt;duration-150&lt;/span&gt; &lt;span class="err"&gt;rounded-md;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.btn&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;@apply&lt;/span&gt; &lt;span class="err"&gt;text-gray-400;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.btn&lt;/span&gt;&lt;span class="nd"&gt;:focus&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;@apply&lt;/span&gt; &lt;span class="err"&gt;z-10&lt;/span&gt; &lt;span class="err"&gt;outline-none&lt;/span&gt; &lt;span class="err"&gt;border-blue-300&lt;/span&gt; &lt;span class="err"&gt;shadow-outline-blue;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.btn&lt;/span&gt;&lt;span class="nd"&gt;:active&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;@apply&lt;/span&gt; &lt;span class="err"&gt;bg-gray-100&lt;/span&gt; &lt;span class="err"&gt;text-gray-500;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.btn-group&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;@apply&lt;/span&gt; &lt;span class="err"&gt;relative&lt;/span&gt; &lt;span class="err"&gt;z-0&lt;/span&gt; &lt;span class="err"&gt;inline-flex&lt;/span&gt; &lt;span class="err"&gt;shadow-sm;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.btn-group&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;.btn&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;:first-child&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;@apply&lt;/span&gt; &lt;span class="err"&gt;rounded-l-none&lt;/span&gt; &lt;span class="err"&gt;-ml-px;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.btn-group&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;.btn&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;:last-child&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;@apply&lt;/span&gt; &lt;span class="err"&gt;rounded-r-none;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;utilities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And our React components look like this:&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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&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;ButtonGroup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&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="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;btn-group&lt;/span&gt;&lt;span class="dl"&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;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&amp;gt;&lt;/span&gt;&lt;span class="err"&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;Button&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;btn&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&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;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;ButtonGroupExample&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Single&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&amp;gt;{" "&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ButtonGroup&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Years&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Months&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Years&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ButtonGroup&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;ButtonGroupExample&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, our JSX &lt;code&gt;&amp;lt;ButtonGroup /&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;Button /&amp;gt;&lt;/code&gt; components are minimal and have only a single class. All the styles are coming from our CSS file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros 👍
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Our JSX looks beautiful, as we can reuse our &lt;code&gt;&amp;lt;Button /&amp;gt;&lt;/code&gt; component and just put it inside a &lt;code&gt;&amp;lt;ButtonGroup /&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Easy to use, if you're already familiar with Tailwind (or CSS frameworks like Bootstrap).&lt;/li&gt;
&lt;li&gt;Component class-attributes are short (instead of tens of classes, vs. examples below).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons 👎
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;We're writing quite a bit of new CSS for our components, instead of just reusing Tailwind's styles. This can quickly bloat our CSS file size.&lt;/li&gt;
&lt;li&gt;Every change to our component styles will cause all of our CSS to be rebuilt, which can make development slow.&lt;/li&gt;
&lt;li&gt;You have to invent new class names for all of your components. &lt;code&gt;.btn&lt;/code&gt; and &lt;code&gt;.btn-group&lt;/code&gt; are quite straight-forward, but sooner or later you might end up with something like &lt;code&gt;.common-navigation__top-right-navigation--wide&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Our component styles are not visible in the components, and we might forget to delete them when we delete the components.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Method 2: Pseudo-Class Variants with Class Overrides
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://codesandbox.io/s/method-2-pseudo-class-variants-with-class-overrides-veh3l?file=/src/App.js"&gt;View in Codesandbox&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this approach, we utilize &lt;a href="https://tailwindcss.com/docs/pseudo-class-variants"&gt;Tailwind's pseudo-class variants&lt;/a&gt; and the &lt;a href="https://github.com/JedWatson/classnames"&gt;classnames library&lt;/a&gt; to override styles for the first and the last button in the. &lt;/p&gt;

&lt;p&gt;In order to do this, we need to add a new component that we will name &lt;code&gt;&amp;lt;ButtonGroupButton /&amp;gt;&lt;/code&gt; (I know, the name sucks!). If we added our pseudo-class variants to our &lt;code&gt;&amp;lt;Button /&amp;gt;&lt;/code&gt; component, it would break when it would be the first or last child of its parent.&lt;/p&gt;

&lt;p&gt;In this approach, our CSS has just Tailwind's basic styles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;components&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;utilities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And our JSX has plenty of classes:&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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;classnames&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;classnames&lt;/span&gt;&lt;span class="dl"&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;ButtonGroup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&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="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;relative z-0 inline-flex shadow-sm&lt;/span&gt;&lt;span class="dl"&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;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&amp;gt;&lt;/span&gt;&lt;span class="err"&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;Button&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;className&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;
      &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;classnames&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;relative inline-flex items-center px-2 py-2 border border-gray-300 bg-white text-sm leading-5 font-medium text-gray-500 hover:text-gray-400 focus:z-10 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-100 active:text-gray-500 transition ease-in-out duration-150 rounded-md&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;className&lt;/span&gt;
      &lt;span class="p"&gt;)}&lt;/span&gt;
      &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&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;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&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;ButtonGroupButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-ml-px first:ml-auto rounded-r-none rounded-l-none first:rounded-l-md last:rounded-r-md&lt;/span&gt;&lt;span class="dl"&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;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;ButtonGroupExample&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Single&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&amp;gt;{" "&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ButtonGroup&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ButtonGroupButton&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Years&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ButtonGroupButton&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ButtonGroupButton&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Months&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ButtonGroupButton&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ButtonGroupButton&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Years&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ButtonGroupButton&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ButtonGroup&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;ButtonGroupExample&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Did you notice, how &lt;code&gt;&amp;lt;ButtonGroupButton /&amp;gt;&lt;/code&gt; uses the pseudo-variant classes &lt;code&gt;first:rounded-l-md last:rounded-r-md&lt;/code&gt; in order to override our button styles? If you try to do this on a normal Tailwind CSS installation, it won't work. The reason is that by default, Tailwind does not generate these pseudo-variants for border-radius classes. We need to &lt;a href="https://tailwindcss.com/docs/configuring-variants/"&gt;configure our variants&lt;/a&gt; in order to add "first" and "last" pseudo-variants. The same is true for &lt;code&gt;first:ml-auto&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In practice, this means that we need to change our &lt;code&gt;tailwind.config.js&lt;/code&gt; "variants" section to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;responsive&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;first&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;borderRadius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;responsive&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;first&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;last&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pros 👍
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Our styles are visible in the components themselves, so we can see what is going on right inside the components.&lt;/li&gt;
&lt;li&gt;We have an explicit &lt;code&gt;&amp;lt;ButtonGroupButton /&amp;gt;&lt;/code&gt; that has the styles specific for button group child buttons.&lt;/li&gt;
&lt;li&gt;If we delete our components, we won't leave dead CSS to our project.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons 👎
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Overwriting &lt;code&gt;&amp;lt;Button /&amp;gt;&lt;/code&gt; styles outside the component itself is quite tedious. For example, we have to use &lt;code&gt;rounded-r-none rounded-l-none&lt;/code&gt; instead of simply &lt;code&gt;rounded-md&lt;/code&gt;. Otherwise, the override won't work.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;Button /&amp;gt;&lt;/code&gt; component has to allow overriding its styles with additional classes, which means that its styles are no longer strictly encapsulated.&lt;/li&gt;
&lt;li&gt;We need to update out &lt;code&gt;tailwind.config.js&lt;/code&gt; variants, which adds to our CSS build time. Moreover, it is very hard to know when we should remove the extra variants when we delete code.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Method 3: Passing Additional Props to Children Using React.cloneElement
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://codesandbox.io/s/method-3-passing-additional-props-to-children-using-reactcloneelement-3x41k?file=/src/App.js"&gt;View in Codesandbox&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What if we would like to get rid of the &lt;code&gt;&amp;lt;ButtonGroupButton /&amp;gt;&lt;/code&gt; component in the above example? This is possible by using &lt;a href="https://reactjs.org/docs/react-api.html#cloneelement"&gt;React's cloneElement&lt;/a&gt;. This trick is found in the wild, e.g. in &lt;a href="https://reacttraining.com/reach-ui/tabs/"&gt;reach-ui's tabs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For this method, our CSS and &lt;code&gt;tailwind.config.js&lt;/code&gt; is identical to Method 2. We only change our components. We remove the &lt;code&gt;&amp;lt;ButtonGroupButton /&amp;gt;&lt;/code&gt; and move its className override to the &lt;code&gt;&amp;lt;ButtonGroup /&amp;gt;&lt;/code&gt; component:&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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;classnames&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;classnames&lt;/span&gt;&lt;span class="dl"&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;ButtonGroup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;relative z-0 inline-flex shadow-sm&lt;/span&gt;&lt;span class="dl"&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;children&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;child&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cloneElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;classnames&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-ml-px first:ml-auto rounded-r-none rounded-l-none first:rounded-l-md last:rounded-r-md&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt;
          &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;)}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&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;Button&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;className&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;
      &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;classnames&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;relative inline-flex items-center px-2 py-2 border border-gray-300 bg-white text-sm leading-5 font-medium text-gray-500 hover:text-gray-400 focus:z-10 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-100 active:text-gray-500 transition ease-in-out duration-150 rounded-md&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;className&lt;/span&gt;
      &lt;span class="p"&gt;)}&lt;/span&gt;
      &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&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;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;ButtonGroupExample&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Single&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&amp;gt;{" "&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ButtonGroup&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Years&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Months&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Years&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ButtonGroup&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;ButtonGroupExample&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pros 👍
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The same pros as Method 2, except we now don't need a separate &lt;code&gt;&amp;lt;ButtonGroupButton /&amp;gt;&lt;/code&gt; for &lt;code&gt;&amp;lt;ButtonGroup /&amp;gt;&lt;/code&gt; to work.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons 👎
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The same cons as Method 2.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;React.cloneElement&lt;/code&gt; is not commonly used and might confuse developers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Method 4: Fully Separate Styles for Button and ButtonGroupButton
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://codesandbox.io/s/method-4-fully-separate-styles-for-button-and-buttongroupbutton-rr5cw?file=/src/App.js"&gt;View in Codesandbox&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If the class overrides in all of the previous methods make you feel iffy, you can choose to have totally separate components for &lt;code&gt;&amp;lt;Button /&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;ButtonGroupButton /&amp;gt;&lt;/code&gt;. This might make you yell, "hey, duplicating components is not DRY!" or ask, "what happens when I update my Button styles?" Neither one of these is a problem.&lt;/p&gt;

&lt;p&gt;The reason is that on a contextual level, our &lt;code&gt;&amp;lt;Button /&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;ButtonGroup /&amp;gt;&lt;/code&gt; components are tightly coupled. In practice, this means that whenever you make a change to &lt;code&gt;&amp;lt;Button /&amp;gt;&lt;/code&gt;, you'll have to check that you did not accidentally break our &lt;code&gt;&amp;lt;ButtonGroup /&amp;gt;&lt;/code&gt;. In most applications, you cannot contextually separate the too, because in most cases, the definition for how the &lt;code&gt;&amp;lt;ButtonGroup /&amp;gt;&lt;/code&gt; should look is something like "multiple &lt;code&gt;&amp;lt;Button /&amp;gt;&lt;/code&gt;s next to each other, but a bit different."&lt;/p&gt;

&lt;p&gt;If you look closely at the code in previous, there are multiple possible changes to the &lt;code&gt;&amp;lt;Button /&amp;gt;&lt;/code&gt; component that would break our &lt;code&gt;&amp;lt;ButtonGroup /&amp;gt;&lt;/code&gt; component:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We use the class&lt;code&gt;-ml-px&lt;/code&gt; that adds a negative left margin to remove a "double border" between the buttons in the group. If we remove &lt;code&gt;&amp;lt;Button /&amp;gt;&lt;/code&gt;'s border or change its width, &lt;code&gt;&amp;lt;ButtonGroup /&amp;gt;&lt;/code&gt; will break and has to be updated.&lt;/li&gt;
&lt;li&gt;We use &lt;code&gt;last:rounded-r-md&lt;/code&gt; to make the last button in the group has rounded borders on the right. If we remove &lt;code&gt;&amp;lt;Button /&amp;gt;&lt;/code&gt;'s rounded borders, &lt;code&gt;&amp;lt;ButtonGroup /&amp;gt;&lt;/code&gt; will break and has to be updated.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because of this contextual coupling, it will be very hard to create these components so that they will never break. You just have to remember to check that the styles in &lt;code&gt;&amp;lt;ButtonGroup /&amp;gt;&lt;/code&gt; still work, whenever you change &lt;code&gt;&amp;lt;Button /&amp;gt;&lt;/code&gt;. Because of this, you might just decide to skip the tedious work of overriding classes and only share the common styles.&lt;/p&gt;

&lt;p&gt;This is what I ended up doing when developing Tailwind button groups for &lt;a href="https://morco.app/"&gt;morco.app&lt;/a&gt;. The result is like this:&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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;classnames&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;classnames&lt;/span&gt;&lt;span class="dl"&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;ButtonGroup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&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="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;relative z-0 inline-flex shadow-sm&lt;/span&gt;&lt;span class="dl"&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;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&amp;gt;&lt;/span&gt;&lt;span class="err"&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;commonButtonClasses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;relative inline-flex items-center px-2 py-2 border border-gray-300 bg-white text-sm leading-5 font-medium text-gray-500 hover:text-gray-400 focus:z-10 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-100 active:text-gray-500 transition ease-in-out duration-150&lt;/span&gt;&lt;span class="dl"&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;Button&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;
      &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;classnames&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;commonButtonClasses&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rounded-md&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
      &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&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;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&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;ButtonGroupButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;
      &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;classnames&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;commonButtonClasses&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-ml-px first:ml-auto first:rounded-l-md last:rounded-r-md&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;)}&lt;/span&gt;
      &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&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;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;ButtonGroupExample&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Single&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&amp;gt;{" "&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ButtonGroup&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ButtonGroupButton&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Years&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ButtonGroupButton&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ButtonGroupButton&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Months&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ButtonGroupButton&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ButtonGroupButton&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Years&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ButtonGroupButton&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ButtonGroup&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;ButtonGroupExample&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pros 👍
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Styles are encapsulated in components.&lt;/li&gt;
&lt;li&gt;Common styles are explicitly stated in code.&lt;/li&gt;
&lt;li&gt;+ Same Pros as in Method 2.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons 👎
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;We still might accidentally break &lt;code&gt;&amp;lt;ButtonGroup /&amp;gt;&lt;/code&gt; styles when changing &lt;code&gt;&amp;lt;Button /&amp;gt;&lt;/code&gt;'s styles. The common variable only gives a hint to the developer but does not "type-check" CSS class relationships.&lt;/li&gt;
&lt;li&gt;If you add special behavior to your &lt;code&gt;&amp;lt;Button /&amp;gt;&lt;/code&gt; component, you will have to duplicate this to &lt;code&gt;&amp;lt;ButtonGroupButton /&amp;gt;&lt;/code&gt;, or have some kind of &lt;code&gt;&amp;lt;BaseButton /&amp;gt;&lt;/code&gt; that is used by both of these components.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;ButtonGroupButton /&amp;gt;&lt;/code&gt; still looks kind of ugly. 😜&lt;/li&gt;
&lt;li&gt;We might not remember to remove the &lt;code&gt;tailwind.config.js&lt;/code&gt; additional variants when we delete code.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;There are multiple ways for achieving the same result when using React with Tailwind CSS. All approaches have their strengths, and what you end up going with depends on the practices that you and your team might like best.&lt;/p&gt;

&lt;p&gt;What method do you think is the best? Why? Do you have a better way of creating a ButtonGroup with Tailwind CSS in React?&lt;/p&gt;

</description>
      <category>react</category>
      <category>css</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
