Software is made for business reasons. The decisions driving the work of software engineering tend to be business decisions, not technical decisions. We see this constantly in software consulting, and observe that time and cost constraints intrinsic to business decisions have implications for the way in which we go about our software engineering work from a technical standpoint.
Business reasons ultimately involve value creation. When existing software delivers the value desired, it is unnecessary to create new software. There is a vast market for new software because the value to be created can be highly situational. Even when available systems lack the desired results, reuse of existing software is most pronounced at the level of individual application dependencies.
Value creation always has time and cost constraints. The ability of a software system to deliver value is irrelevant if its development consumes more resources than are available. Scope and quality are additional factors that can be adjusted to identify a set of possibilities satisfying time and cost constraints, but time and cost tend to be most prominent in software consulting work.
Time and cost constraints directly impact the writing and maintaining of code. The most time-consuming and expensive activities of software engineering are writing and maintaining code. Writing and maintaining code is expensive, and significant time is allocated to that work. This compels us to link code decisions to constraints of time and cost.
Reusing code reduces the time and cost required to write new code. When desired results are available with existing code, it is often wasteful to write new code. The more general the value to be delivered, the more likely it is that an existing solution is sufficient. This extends to the smaller parts composing a larger system. Tools for authenticating users, facilitating API endpoints, and presenting data in various formats (and many other capabilities) are readily available with little or no customization required. Such third-party dependencies enable maximized focus on unique value creation within defined time and cost constraints.
Reusing code can increase the time and cost required to maintain code. Features are the number one source of bugs and security vulnerabilities. Features delivered by third-party dependencies are still features, and they become our responsibility when we add them to our system. Any dependency added to a system could constitute the weakest link introducing the greatest source of instability.
Most work with code involves modifying existing code. The excitement of creating something brand-new quickly gives way to the reality of working within considerable constraints of an existing system. Even when the increments are implemented rapidly, incremental development builds on existing systems.
Reusing code has become a mature part of software ecosystems. Tools for facilitating code reuse have advanced significantly over the last few years, with package managers like npm, RubyGems, and pip enabling software engineers to specify application dependencies with precision and confidence.
Maintaining reused code remains a difficult task. Tools for keeping dependencies updated and surfacing potential issues with third-party code are chronically underdeveloped. It is extremely common for dependencies to be defined once upon initial use, after which no further thought is put into maintenance.
Reusing code bears the same responsibility as writing code. Codebase changes that add or modify interactions with dependencies are typically minimal—dependencies are listed in a dependency file, and dependency invocations indicate only method signatures from the dependency. While this is normal and desirable from a standpoint of deferring to reused code, it fails to communicate the size and scope of a dependency. The following questions and examples describe important considerations in taking on the responsibility of working with a dependency:
Reused code may add unnecessary complication. A guiding principle of reusing existing code wherever possible can lead to reusing code where it is better simply to reuse an idea:
The best code reuse delivers reliable results that would otherwise be unreliable. Concerns about time and money affect us all. Unless there is a truly compelling reason, programmers should not work directly with time. The same caution should be applied to working with monetary units. Custom validations for email addresses are almost certainly wrong. Rules for phone numbers are nontrivial. Dependencies like Moment.js, Joda Time, and accounting.js isolate significant incidental complexity to a reliable third-party library.
Reusing code means accepting responsibility for maintaining it. We are committed to evolving software systems with maintenance in mind, and built Dependable to provide automated insights into new versions of Ruby gems. Tools for reusing code are mature and popular, now the challenge is to make it as accessible to maintain reused code.