By Pete Oliver-Krueger
Chief Librarian, The Library of Agile
TL;DR Summary
Step 1: Team A writes the code to fit their actual need with real external customers.
No reusable code. No configuration for reusability, yet.
Don’t do it. Don’t plan for future reusability. Don’t lay the groundwork. Don’t add a feature flag.
Just don’t do it.
Build it as it needs to be built to support the external customer user.
Step 2: Team B does Step 1 and writes the code to fit their need.
(Team B could really be Team A again, just using the code in a different area of the application.)
Again, don’t make it reusable, yet! It’s important that team B write the code as it needs to be built to support their external customer user for that specific test case!
Architects write a test framework around team A’s code and team B’s code.
Architects compare team A’s code vs team B’s code.
Architects create a reusable version that works in both places, if possible, with configuration variables as needed.
(If not possible, it’s not a candidate for reusable code. Do NOT make the external customer user suffer for the sake of reusability.)
Architects test reusable code within the test framework.
For next release, architects swap in v1 of reusable code and remove original code, and teams A and B run Regression Testing.
(If problem, swap back original code and/or fix bugs.)
Step 3: Team A or B can make proposals to edit reusable code with mentoring from Architects.
(If a new team that has not previously used the reusable code, they go through Step 2 with an Architect.)
Team makes edits (if necessary) to the reusable component by pair programming with an Architect, creating v2 of reusable code as necessary.
Architects update test framework, if necessary.
For next release, Architects swap v2 of reusable code into teams A and B code for Regression Testing.
(If problem, swap back v1 code and/or fix bugs.)
Step 4: Any team with experience in the reusable code can make changes, but they should still ask Architects for advice before doing so.
(Any team that has not previously used the reusable code goes through Steps 2 and 3 first.)
Experienced team members make the change, update the test framework, if necessary, and submit pull requests to the Architects for a new version.
If rejected, Architects work with team as needed to create the new version.
When accepted, Architects work with the other teams to hot swap the new version into the other codebases and Regression Test.
(If problem, swap back previous version and/or fix bugs.)
The Problem
I’m going to go ahead say it, “most teams do reusable components wrong.” I can’t back that up with evidence… maybe one day… but I have experienced this problem too many times to assume the opposite. Some teams use really well-design component libraries, created and supported by a community of developers. I’m not talking about those reusable components. I’m talking about the teams that try to build their own reusable components.
Now I can’t tell you which architectural design patterns or coding strategies will turn your code into reusable code. There’s an old Buddhist joke, “The key to happiness is one thing... I can’t tell you what that one thing is, but when you find it you will have true happiness.” Buddhism sees itself as the way to help you find your way, not as your destination.
The same is true of architecting reusable components. The key to reusability is different for different situations. The world of architecture refers to these as “Design Patterns”, going back to a book of the same name Design Patterns written by the “Gang of Four”.
What I can share with you is a consistent way to uncover the right Design Patterns to use to create the reusable components that you need. It involves four steps.
In my hubris as a young architect, I walked these steps backwards. The first time I did it, it worked great! I was so proud of the reusable code that I had created. The second time I walked backwards on that path, it wasn’t a bad experience either. By the third and fourth times that I walked those backwards steps, I was already starting to feel the pain of late nights, endless recursive bug hunts, and huge buckets of “tech debt” that I knew I would have to refactor some day, just not today.
Now I can’t tell you how many times I walked down that path in the wrong direction, but I can tell you how great it felt when I finally turned around! It didn’t feel great at first, because it felt wrong. It felt backwards. I almost stopped, but the old path was causing so much angst and frustration that I pushed through. By the time I stepped off that last step in the right direction, I knew I was now pointed in the right direction, and I knew why.
If my experience and my pain can save you from at least making the same mistakes that I did, let me help you get ahead in the game. Follow these 4 steps to creating reusable components and you will save yourself months of unnecessary work and emotional toil.
Step 1: Be Realistic and Spare Yourself Future Pain
Guidance
Team A writes the code to fit their actual need with real, external customers.
No reusable code. No configuration for reusability, yet.
Don’t do it. Don’t plan for future reusability. Don’t lay the groundwork. Don’t add a feature flag.
Just don’t do it.
Build it as it needs to be built to support the external customer user.
It’s Not a Prediction If You Force It To Come True
The secret to creating reusable code is to not make reusable code! I’ll say it in another way for emphasis because it’s that important. The secret to creating great reusable code is to keep it realistic.
Here’s the thing that I experienced, and that I have experienced with every other architect of reusable code with whom I’ve worked. We’re really good at imagining multiple future scenarios where we our code can be reused. We can map these scenarios in great detail and predict how they will be used, including what the pitfalls will be. This ability of future-sight is the reason we get chosen to be architects in the first place.
If this sounds like you, ask yourself, “Which of those future uses actually happened?” If you have reusable code that has already been deployed in the real world for several years, add some trackers and see how many people actually use each of the scenarios that you planned out. It’s not enough to search your code bases for how many times those parts of your code are called from other components. You have to put trackers in the real, live code that is being used by real-world external users.
The thing that trips up architects is that because we’re so proud of our code, we push our teams to use it. Sometimes they don’t have a choice. So we can always find evidence of where our code is being used, but over time, if it’s too difficult to use, teams will find an alternative. If it does get published to the real-world application, but it makes users lives hard, they will also stop using it. If you only use it for internal applications, you can force your employees to use your code, but over time, if it prevents them from effectively supporting your external customers, someone will eventually sunset your application and switch to a new platform. This is why the only true measure of reusability is logs from trackers in real-world applications with external users.
If you take this step of measurement, I’m going to guess that you will find that 50% of your planned use cases of a reusable component are not being used with enough regularity. Probably, on average, only 25% of your planned architecture is actually being used on a regular basis. You might also want to ask yourself how many of your future features have been started but not completed?
In the meantime, the developers who are using your “reusable” component are having to jump through hoops by updating configuration variables and executing additional, unnecessary steps to debug and exclude scenarios that are never going to happen with their users. Not such a big deal if you only have a few users, but as your application grows and scales (which we hope it does), the cost of each of these extra computer cycles with start to add up exponentially. We’ll talk about this more with the Degrees of Maintenance Theory in Step 2.
If you are really unlucky, you may have even made it impossible for your developers to support an actual scenario that they are experiencing right now, in favor of leaving room to handle a future scenario that may never come to pass. I’ve lost count of the number of times I’ve encountered this situation with clients that I’m coaching.
If this does not sound like you, though, then congratulations! You have probably already learned the lesson behind step 1.
Step 1: Code for the real use cases that you already have.
Step 2: Should It Be A Reusable Component?
Guidance
Team B does Step 1 and writes the code to fit their need.
(Team B could really be Team A again, just using the code in a different area of the application.)
Again, don’t make it reusable, yet! It’s important that team B write the code as it needs to be built to support their external customer user for that specific test case!
Architects write a test framework around team A’s code and team B’s code.
Architects compare team A’s code vs team B’s code.
Architects create a reusable version that works in both places, if possible, with configuration variables as needed.
(If not possible, it’s not a candidate for reusable code. Do NOT make the external customer user suffer for the sake of reusability.)
Architects test reusable code within the test framework.
For next release, architects swap in v1 of reusable code and remove original code, and teams A and B run Regression Testing.
(If problem, swap back original code and/or fix bugs.)
People Should Want to Use Reusable Components
Even though your developers on the team may not have the skill yet to write reusable code, seeing how they want the code to work conveys very valuable information to an architect. Now I am not saying that your should design your reusable code around your developers’ coding styles. What I am saying, though, is that reviewing real-world use cases will tell you a) what functionality the real external users are already asking for, and b) the thought processes of your developers.
Half of the job of reusable code is to create something people want to use. This means both that it provides value in and of itself, and that developers want to (and understand how to) use it. I am sad to say that I have created some awesome reusable code could consistently produce working products 10x faster, but only in my hands. It was never used by anyone else because other developers didn’t want to spend the time to learn how to use it. In contrast, when I looked at what kinds of programming styles developers already were excited about using, and designed my code to be used in the same way, I had much better adoption rates with a lot less effort on my part.
The other half of the job of reusable code is to make the lives of everyone working on the code easier. If it doesn’t do that, it shouldn’t be turned into reusable code. What is the benefit of your reusable code? Does it run faster? Does it help developers build products faster? Does it make the software more secure? Does it make the code more readable, and hence faster and easier to maintain? Does it augment other code and make it more powerful? Does it make coding more enjoyable? Sometimes as architects we get so caught up in what we *can* do that we forget to ask if it is something that we *should* do.
Degrees of Maintenance Theory
Let the “Degrees of Maintenance Theory” be your guide on whether or not something should be turned into reusable code. Have you ever had a bug to fix that you were sure was going to take 15-30 minutes to fix, but the cascading impact of that bug ended up costing you hours? If so, you have felt the impact of your application’s "Degrees of Maintenance".
I discovered the Degrees of Maintenance when I was working for a client debugging an application that I had not written. I needed to change the name of a field in the database. It should have been 15 minutes for a name change, search-and-replace, and recompile. 2½ hours later, I finally got the code completely compiled and running! I went back to figure out why.
I started from the database and traced the journey of the data through the code. The database was connected to the application through Java-based dependency injection. That connection established a DAO (Data Access Object) which was created using an Interface class (even though it was a singleton object) because that’s how that developer had been taught to build things. The DAO was connected to a REST API object which was also built using an Interface object. On the front end there was an object making the REST calls which passed the data to a controller object that passed the data to a data-binding object that updated a JavaScript DOM object that had a listener that executed several other functions.
Even if you don't understand all of those terms, perhaps you're still able to feel my exhaustion. At final count, the data passed through 10 layers of "reusable" objects. I realized that if I spent the estimated 15 minutes updating field names and checking each of the 10 layers of code for bugs, the total amount of time equaled 2½ hours, matching the actual time spent!
The Degrees of Maintenance Theory says that every estimate should be multiplied by the number of layers that data passes through to move from the user to the database or vice versa. Looking at the Theory in reverse, the corollary is that any new layer, such as a reusable object, will double the maintenance time of an application and should therefore compensate by increasing the speed of development by at least double. If turning code into a reusable component doesn’t cut your development and/or maintenance time in half, you shouldn’t make it reusable code. Let it remain as custom code.
For example, in the code that I had taken over, there were about 4 to 6 unnecessary layers. Object Interfaces in Java were designed to save time when instantiating multiple classes that shared the same properties and methods. In both uses for this application, the interface object was only used for one object, and therefore saved no time. Likewise for the interface object for the REST API object. Even the DAO object didn’t save any time. In fact, when I timed myself using both approaches, it took more time to write the code using the DAO object than it did to write a SQL statement to update the database directly from the REST API object. On the front end, the data-bind code did save enough time to be useful, but it also acted as a controller making the other central controller object unnecessary and slower to use.
Step 2: Reusable Components must both add value, and at least double the speed of writing code.
Step 3: Train Developers to Think Like You
Guidance
Team A or B can make proposals to edit reusable code with mentoring from Architects.
(If a new team that has not previously used the reusable code, they go through Step 2 with an Architect.)
Team makes edits (if necessary) to the reusable component by pair programming with an Architect, creating v2 of reusable code as necessary.
Architects update test framework, if necessary.
For next release, Architects swap v2 of reusable code into teams A and B code for Regression Testing.
(If problem, swap back v1 code and/or fix bugs.)
Code Libraries As a Phase Gate
Consider this real-life scenario that I’ve seen in organizations. The team building the application for the external users makes use of an internal library of code. We’ll call that team the “application team”. The library they use is maintained by its own team that knows that code better than anybody else. We’ll call them the “library team”. They are the right people to make updates to the library to ensure that one team using the library doesn’t mess things up for the other teams using the library. It sounds perfect.
The application team has an idea for a new feature and they start working on it, but quickly realize that they should use the library code, except that it’s missing a feature that they need so they contact the library team. The library team adds it to their backlog and prioritizes it for the next sprint where they implement the new feature and deliver the updated library code back to the application team. The application team prioritizes integrating the new library code into its next sprint and publishes the new feature… 6 weeks after they had the idea.
This scenario has introduced what we can a “Phase Gate” in Agile terminology. The code must pass through a “gated community”, namely the library team, and the change approved before it can be implemented and the new value delivered to the customer. The library team, though, doesn’t know or work with the customer so they don’t understand the urgency or the pain of that customer and aren’t as motivated to solve it as the application team. Even in the best-case scenarios that I’ve witnessed — in Scaled Agile teams using Program Increment Planning, a dependency board, and advanced planning — the fastest delivery time for a new feature is 4 weeks. In contrast, with those same teams, the fastest delivery time for a new feature idea that doesn’t use reusable code is 1-2 weeks. On average, though, the actual delivery time for most changes requiring reusable library code is at least 2 months, from idea to demo.
Developers Build, Architects Refactor
Consider this alternative scenario. The application team has an idea for a new feature that requires modifications to the reusable code. They reach out to the library team and setup a pair programming session, where they make the necessary changes they need and complete the change within the same sprint. The time from idea to demo is 2 weeks. The Library team in their next sprint updates the library code and makes it available to other application teams in their next sprint 2 weeks after that. The time from idea to demo for the original application team is still only 2 weeks.
Consider this case where the architects are unavailable. The application team has an idea for a new feature that requires modifications to the reusable code. The library team is unavailable, so the application team hacks the library and gets it to do what they need it to do and sends their code to the library team. The application team delivers the new idea is 2 weeks. The next sprint, the library team reviews the code from the application team, creates a test framework around the code to verify that it produces the same results. They then refactor the code to make it better and more efficient, while also using the test framework to verify that it won’t break the original application. In the next sprint, they deliver the refactored code to the application team who swaps out their custom code for the refactored code. The time to deliver value to the user is still the original 2 weeks. The time to deliver approved, refactored, quality code to the external customer is still 6 weeks, same as before, but the external user has received the value of the feature 4 weeks earlier, and then the application gets even better at 6 weeks out.
So your choice is to either get value to your users in 2 weeks and deliver higher quality (that they might not even recognize) after 6 weeks, or you can deliver the customer value along with the high-quality code both at 6 weeks. Which scenario is better?
Use Pair Programming To Upskill Developers
Whenever possible, I always encourage my teams to use Pair Programming with a developer and an architect when making a change to reusable code so that the developer has the chance to learn how the architect thinks. The architect (or library team) is almost always comprised of more-highly-skilled developers, but if they always operate in isolation within their own team, there is no opportunity for less-experienced developers to gain the new experience that they need to grow. Organizations that have separate library teams very often are also slower to mature. Dedicate time for an architect or senior developer to work on code with the less-experienced developers to accelerate your organization.
Step 3: Train application teams to make their own modifications to reusable components intelligently.
Step 4: Create a Community of Practice
Guidance
Any team with experience in the reusable code can make changes, but they should still ask Architects for advice before doing so.
(Any team that has not previously used the reusable code goes through Steps 2 and 3 first.)
Experienced team members make the change, update the test framework, if necessary, and submit pull requests to the Architects for a new version.
If rejected, Architects work with team as needed to create the new version.
When accepted, Architects work with the other teams to hot swap the new version into the other codebases and Regression Test.
(If problem, swap back previous version and/or fix bugs.)
Communities of Practices Should Replace Library Teams
After you have a few application teams that have gone through mentoring with the library team (or the architects), the biggest remaining obstacle is one team wanting to make a change to the code that doesn’t work for another application team. Normally these debates happen within the members of the library team, with one library team member acting as advocate for application team A and another library team member acting as advocate for application team B. It’s always better to have original requestors in the discussion rather than playing the Telephone Game.
I frequently recommend that instead of having library teams, you form a library-centered Community of Practice. Each application team should have an experienced developer who specializes in maintaining the library. Those developers then have a weekly or bi-weekly meeting with other experienced library developers from other application teams. At the Community of Practice they share new ideas for library changes from their own teams’ needs, and resolve any differences in uses between the teams. When the differences become too great, they request the assistance of an experienced architect who can either resolve the problem or decide to fork the library as necessary.
The added benefit of using Communities of Practices instead of library teams is that you either get to redistribute experienced developers back into other teams which boosts productivity, or you get to form a new application team which also boosts productivity. When done right, a reusable library can be maintained by one or two supervising architects and a Community of Practice.
The main exception to this rule is when you have an application team that uses the reusable library, but so infrequently that they forget how to do so. In these cases, channeling the library update work to another team with a library expert is a good solution.
Additional Notes
“Internal Customers” vs External Customers
When I talk about “team A” and “team B” and their customers in my descriptions, I am referring to teams that are developing code for external customers, not “internal customers”. It is important that the features built into reusable code are directly traceable to the organization’s business model. If your team only works with “internal customers”, then start with a feature that helps an “internal customer” support an external customer.
If you ignore this advice (which you certainly may), you may not feel the impact of this choice right away. You may not even feel the impact for years, but eventually, you will discover the impact of prioritizing “internal customers” and you will pay the price later for that choice. It has always happened in my 15 years of writing code and coaching, with every team that I’ve encountered.
To go even further, I would really like people to stop using the phrase “internal customers”. If they’re not paying for anything, or in some way impacting the business model, then they are not customers. I train my teams to use the phrase “internal partners” instead. If you write code to support another part of the company, you are both internal partners in support of your external customer.
Comments