# Developer Experience (DX) Creating an efficient and enjoyable development environment is essential for producing high-quality software. ## What is DX? There is no shortage of articles on the internet about developer experience, but to make a succinct statement here: - Developers thrive when equipped with high-quality tools and efficient workflows. This allows them to focus on solving meaningful problems rather than battling inadequate resources or cumbersome processes. - Having a great developer experience attracts and retains top talent. Developers (the good ones, anyway) love to feel productive. GitHub's [research on DX](https://github.blog/2024-01-23-good-devex-increases-productivity) shows that productivity increases when developers experience: - Reduced cognitive load through clear documentation and seamless processes. - Fast feedback loops, such as quick builds and tests. - The ability to achieve a flow state, with consistent blocks of distraction-free time. For a detailed examination of developer productivity metrics, refer to this [study of developer productivity metrics at 17 companies](https://newsletter.pragmaticengineer.com/p/measuring-developer-productivity-bae) The following principles support an excellent developer experience. ### Keep Feedback Loops Fast Feedback loops provide developers with new information in response to their actions. Questions like "Does my code compile?", "Did this change fix the bug?", and "Did I break anything?" are answered through these loops. All feedback loops should be as fast as possible. #### Fast Builds Build times affect the productivity of every developer every day, so any slowdown in this feedback loop deserves special attention. Addressing build performance early prevents it from spiraling into hours-long delays that disrupt workflows. Benefits of fast builds include: - Frequent builds that catch bugs early. - Reduced downtime waiting for results. I've witnessed build times gradually worsen until they reached hours. This severely impacted productivity, as developers struggled to manage context switching effectively. Prioritizing build speed can prevent this kind of spiral: we should all resist the urge to shrug and say "It's ok if it's only a little slower now." #### Other Feedback Loops Developers constantly ask, "What happens when I change this code?" Fast answers improve productivity. Some examples of this feedback and how to speed them up include things like - For UI changes, tools like Vite's HMR provide near-instant hot reloads. - Debugging should be possible directly in the IDE. I've worked on large systems where debugging relied on logs, limiting insight into issues. Efficient debugging workflows save significant time. - Seeing test results must also be quick. Use [incremental builds](https://blog.gradle.org/introducing-incremental-build-support) or run individual tests instead of running the entire suite. ### Keep Tech Up To Date Keeping languages, frameworks, and dependencies current ensures access to bug fixes, security patches, and opportunities to simplify your code. Updated runtimes improve performance, while modern build tools often include efficiency enhancements. This translates to spending less time discovering buggy behavior in your technology and spending more time on features. Embrace your upgrades. #### Avoid the Bleeding Edge Adopting beta versions can lead to wasted time on bugs that stable releases would resolve. Stick to GA (General Availability) versions unless there's a compelling reason to adopt pre-releases, such as essential features not available elsewhere. ### Reduce cognitive load #### Eliminate noise - Eliminate compiler warnings and resolve or remove TODO comments. When warnings and comments become noise and are ignored, they can hide real issues. - Eliminate flaky tests. When tests failures become noise and are ignored, they can hide potentially dangerous issues. I have witnessed developers become overwhelmed by the frustration of flaky tests, and just rerun broken builds until they get a green build so they can merge. But a flaky test is a broken test, and it should be fixed or deleted. - Remove dead or unused code. There is always a temptation to keep something "just in case" when really the principle of YAGNI (You Ain't Gonna Need It) should be your guide. The cognitive load of seeing the code, remembering that it's unused (or worse, wondering if it's unused) and deciding to move on, are all work that a developer has to do that adds to the overall cognitive load of development on the project. #### Maintain Clean Code - Prioritize clarity over cleverness. Use comments to explain why, not what. - Reduce Cyclomatic Complexity (the number of paths through the code) and Cognitive Complexity (constructs that make code difficult to follow such as many nested loops, or having many places in the code to modify for a single change) - Maintain [high cohesion](https://en.wikipedia.org/wiki/Cohesion_(computer_science)) and [low coupling](https://en.wikipedia.org/wiki/Coupling_(computer_programming)) in your source code. Oversized files often indicate code doing too much. For example, files exceeding 1000 lines could be evaluated for refactoring. I have seen files with 5000 lines or more, and they are extremely difficult to reason about. - Use consistency of coding approaches - if you change an approach in one place, change it everywhere before considering it done and moving on. Projects with multiple techniques for the same task are confusing and lead to inefficiencies. #### Work in small steps - Break down work into manageable units of work with clear boundaries and acceptance criteria. Small pieces of work are easier to understand and reason about, are easier to code, and are easier to test Use the classic story-splitting flowchart for help if you need ideas on how to split stories into smaller pieces. ![](story-splitting.png) - Use User Story Mapping to determine what is most valuable, prioritize work that unlocks future capabilities. - Write the simplest solution that works. Mark Twain's famous quote, "I didn't have time to write a short letter, so I wrote a long one instead" applies to writing software. Be aware that complexity can be deceptive, appearing manageable until revisited months later, and strive to write for the future reader. #### Protect the Process - Prevent problems by performing Root Cause Analysis (RCA) for recurring issues. Techniques like the [Five Why's](https://en.wikipedia.org/wiki/Five_whys) can help identify underlying causes. - Document institutional knowledge thoroughly, ensure shared understanding through discussion. Because shared documents are not shared understanding! - Foster a feedback culture where developers openly share ideas for improving tools, processes, and the codebase. #### Use the Best Tools - Invest in high-quality tools, including IDEs and AI tools like GitHub Copilot. The productivity gains far outweigh the costs. - Use AI thoughtfully as a brainstorming and troubleshooting aid. For example, I’ve used AI to evaluate technical decisions by framing questions around simplicity, testing complexity, and cognitive load. - Leverage automated security tools, such as Dependabot, to handle updates and vulnerabilities efficiently.