Technical Debt of Using Code Behind
I recently wrote a post entitled "Why Use Code Behind?". It outlined several reasons why .NET developers use code behind for the logic of their C# or VB.NET applications, not just for UI management.
This post looks at the down side of using code behind for application logic … in two words: Technical Debt.
"The debt can be thought of as work that needs to be done before a particular job can be considered complete. If the debt is not repaid, then it will keep on accumulating interest, making it hard to implement changes later on. Unaddressed technical debt increases software entropy."
From Ward Cunningham, 1992 (as quoted in Wikipedia):
"Shipping first time code is like going into debt. A little debt speeds development so long as it is paid back promptly with a rewrite… The danger occurs when the debt is not repaid. Every minute spent on not-quite-right code counts as interest on that debt."
Bottom line … using code behind for the logic of an application adds to your technical debt.
Some situations, such as when building a Minimum Viable Product, incurring technical debit is a logical choice. Why not incur debt if the team is not even sure if they are building the right thing?
In other situations, the team may not be aware of the technical debt they are accumulating. Not until the application complexity, testing process, or bug count become unacceptable, or worse the project fails.
Two areas where technical debt becomes apparent are maintenance and testing.
In most cases, writing the initial code is only but a fraction of the lifetime of that code. The vast majority of time is spent on extending, enhancing, modifying, or fixing it.
Writing application logic in the code behind makes maintenance much more difficult (adding interest on the debt). Code behind:
- Prevents Reuse. For example, say a developer has code in code behind that opens a file. If a later feature also needs that same logic, it will need a copy/paste to reuse it.
- Encourages Duplicate code. See the prior example. And if a bug is later found in a routine, will the bug in the copies be found and fixed?
- Adds Complexity. Instead of working with many small routines that each perform one task, code behind often leads to writing very long methods that contain all of the logic required for a particular event. For example, a Save button’s code behind might perform validation, create a transaction, save to the database, generate an email, and print a receipt.
- Prevents Automated Code Testing. See the next section.
By writing logic in components, those components can be reused, preventing duplication. And the methods within the components can be single-purpose, making them less complex and easier to maintain and test.
Automated Code Testing
Writing code without unit tests adds to the interest on the technical debt … and not like a home mortgage 3 or 4% interest … more like a credit card’s 35% interest or more. Let’s look at why…
A developer builds a feature and provides it to someone else for testing. That tester may spend many hours testing all of the possible paths through the code. The code is released and work starts on the next set of tasks.
When that work is finished, the tester needs to test every single path again: all of the original code paths plus all of the new ones.
In most cases, the developer ran over the schedule, so the testing schedule is cut. The tester needs to test more in less time. And over time, the number of paths to test grows.
Using the built-in MSTest tools or any testing tools compatible with Visual Studio, such as NUnit, you can easily create automated code tests for your logic. But not if that logic is hidden inside a code behind file.
By writing the code in components, the developer can use the automated code testing tools within Visual Studio to test the application logic. And more exciting … the developer can easily retest all of the original functionality with those tests as the code changes over time.