TDD Inside, Outside, Upside Down
Test-Driven Development: Really, It’s a Design Technique
Test-driven Development (TDD) is by now an established technique for delivering better software faster and sustainably over time. TDD is based on a simple idea: write a failing test before you write production code itself. Need new behavior? Write a failing test. However, this “simple” idea takes skill and judgement to do well. TDD is really a technique for design. The foundation of TDD is using small tests to design bottom up in an emergent manner and rapidly get to some value while building confidence in the system. A better name might Test-Driven Design. As a design method, it’s about focus and simplicity. The goal is to prevent developers from writing superfluous code, code that’s unnecessary to for the delivery of value. It’s about writing the least amount of code needed to solve a particular problem.
Who Can Play TDD?
The motivation to consider TDD often comes when the organization realize that they may have some serious legacy code problems and want to determine what can be done to mitigate these issues, prevent them from reoccurring in the future and ensuring new problems are not being introduced.
In general, TDD practices should ideally be adopted by developers and engineers. The stakeholder who will get the most value from TDD is a senior technical leads or engineering management at various levels, though the whole team and organization benefit. TDD is often undertaken from the perspective of engineering management, but also, many times, from the product side, i.e. how can we set up product development in a way that sets people up for good engineering practices. There exists a definite and proven strong synergy between these two perspectives.
TDD may prove to be even more needed when there are several projects taking place concurrently as part of a large enterprise modernization initiatives. In the past, we may have referred that to a rewrite, but a lot of software is now moving from internal operations to the cloud, and in doing so, it creates a unique opportunity and a justification and a validation of the need for a rewrite parts of the software that is being migrated. A perfect time to introduce a TDD practice.
TDD is valuable because it ensures that we’re not writing an extensive amount of redundant extra code, which, in turn, minimizes defects. It keeps code minimal. It puts the focus on the stories as opposed to simply rewriting code. TDD can also help prevent or mitigate our code ending up as a kind of big ball of mud. It helps in removing getting past behaviors that yield legacy code replacing them with new and healthy habits:
-
you’re not writing reams of extra redundant code
-
in the process of writing that code you’re leaving a fair number of tests that give you confidence in the high initial quality of that code, so delivery becomes more of a one and done activity.
-
We shift our focus to stories, generate less rework and are forced to manage less defects between QA department and development department.
The Challenges of TDD
TDD is a challenge to introduce and adopt. You’re asking of developers to totally invert the way they’ve created software, sometimes for many years, successfully. The developers have to write a small test first, be super minimal and non-speculative about what you’re doing and let the design emerge rather than resorting to the old way of doing things, where significant time was spend on getting the design right while relying heavily on things like a debugger, big upfront well-defined and granular architecture and design and an upfront perfect understanding of the requirements. TDD asks your team to adopt and shift their mindset in a dramatic away. Delivery becomes a series of little experiments where the team gains confidence they can change their mind later; the code is open to evolution.
Refactoring is a significant component of an effective TDD practice. It lets us bring code to a state where, when needed, you can change its structure and perform the necessary refactoring. And those two basically go hand in hand or if you want, the peanut butter and chocolate of engineering practices. The challenge here, of course, is confronting developers with successful track record of software delivery and convincing them there’s even more success to be had if they adopt this new way of doing things.
And let us not lose sight of the fact that there is a tool chain involved in the process. You are now making the learning curve even steeper by introducing a test runner, refactoring tools, and perhaps even some code coverage and analytics tools that will add additional transparency to what is going on with the code. There’s some learning with this new tool chain (which developers are often faced with and accustomed to), but now coupled with a mindset shift.
TDD does have a fairly steep learning curve. It requires a level of finessing and mastery. It’s not a difficult technique to get started with but does take some time and focus to perfect. It can take anywhere from a month to three months to get a team up and running with the practice, get the moves down and learn about some of the common pitfalls that lead to brittle test suites. The learning curve may often introduce conflict with management who may be somewhat more concerned with performance metrics such as velocity and capacity. Adoption works best when management understands the value, the investment, and supports the team’s commitment to the practice.
One of the ways to evaluate the impact of TDD is to verify that that you are making consistent progress in creating a maneuverable and truly agile architecture. Adopting and implementing these practices is likely to slow you down initially. The upside is that you gain speed quickly through sustainable, predictable delivery pace. You consistently deliver quality software, provide the right solutions, in time and within budget – it might be the sort of practice that people need to experience for themselves to really understand, see the value, and overcome skepticism.
So Why Should We Do TDD?
Applying TDD yields several benefits. First, it will increase the focus of your delivery talent and their alignment to your product impact goals. Delivery will become more efficient and will quickly deliver the business results.
Even though there is an initial learning curve, many teams report significant reductions in defect rates, at the cost of a moderate increase in initial development effort. These initial overheads are more than offset by a reduction in effort in projects’ final phases. TDD will also yield a more sustainable codebase in matter of months. You get good at changing your code to fit new learnings or new opportunities through refactoring safely covered by tests.
Another of TDD’s benefits is that you will get technical documentation of your code in the form of tests. What to know what this component does? Look at the tests! This documentation doesn’t drift from the code. If the test passes, the documentation is relevant. We don’t need to have the agile pendulum swinging too far toward no documentation. As the team grows and encounters the need to scale, this documentation will greatly help to onboard new developers.
TDD is highly complementary to practices such as pairing, code review and others. It comes from eXtreme Programming (XP) and the practices are designed to complement each other. It can also help reduce the technical debt and risk involved in rewriting large portions of existing mission-critical applications. TDD will help you to develop more consistently, reduce time to market, help you adapt your software to changing business needs and help you avoid analysis paralysis; the cost of changing is much, much lower.
TDD and its Interface with Product Development
When we discuss product development, we usually start from the business goals and product goals. This is typically followed by some mapping out and focusing on users. We then extract the user stories from that map and ask, how do we know we’re done? This is what we call the acceptance criteria., which in turn serves as the fuel for TDD. You can then take one of those stories as an example and get those passed through a series of small, more technical tests, that emerge from the TDD practice and gives you focus on what it takes to ship the feature. This can also form an intersection point and encourage healthy cooperation between the business and the product teams.
TDD is truly about transitions vs. handoffs. We’re transitioning by describing our whole narrative surrounding our product. We’re describing the user stories we’d like to work on in the sprints, and we integrate this into the code-base when we start employing this tactical TDD practice that is bound yield better quality code.
The magic happens when you connect product discovery to delivery in that manner. It yields high performing product development, discovery and development team in a holistic fashion.
Top 4 Take-Aways:
-
TDD and refactoring go hand in hand. The two are inseparable. Refactoring helps minimize the amount of unnecessary code that makes its way into production by minimizing the amounts of defects that you’re coping with.
-
TDD makes for a code base that can stand the test of time and can move and pivot alongside your market and in conjunction with you learning through product discovery. TDD is a refactoring
-
TDD gets you to a very high initial quality. You’ll get increased and valuable learnings in terms of the product you are developing, while at the same time decreasing your defect rate. And that is pretty cool!
-
When you interface TDD with product development, you increase the quality or development and discovery in a synergistic fashion.
So, What’s the Best Way to Learn More about TDD?
If you’re interested in learning more about this design technique, a great place to start is with a book called Object Oriented Software, Guided by Tests (Freeman & Price). Ideally, you would create an internal team that intentionally chooses to adopt these practices, having access to someone that’s done it right in previous implementations, and committing to the fact that you may be somewhat slower in delivering during the early learning phases, which could take four to six weeks.
So, why not give TDD a chance?