Developing Agile-Driven Software whilst adhering with Best Engineering Practices

Wimal Perera
Lexicon Digital

--

“Software Engineering” is different from most of the other engineering domains because;

  • it takes “significantly large amount of time” (at least months or in some cases years) to design, develop and deploy “software”-based “IT (Information Technology) Systems”
  • “product requirements” of an “IT System” often “change from time to time during its entire life cycle” (i.e. during design, development and even after deployment)

Hence “software engineering” needs to have its own set of “endemic” engineering practices in order to manage an engineered product (i.e. IT System) “over a long time with constantly changing product requirements.

In order to facilitate “software engineering” for the above challenges, from a client/customer interfacing point of view; the “software development process” have evolved from the obsolete “waterfall methods” to the currently accepted “agile-scrum methods” over the last decade.

Figure 1: A “For Dummies” Illustration of Agile Software Process from a Sprint’s Perspective

The “agile-driven software processes” focus on delivering a software by means of “potentially shippable product increments carrying new feature additions” once every shorter time span (usually once a fortnight called a “sprint”) to the overall delivery. Hence the “requirement changes” (logged as “new or refined user stories”) are “re-captured” and “re-prioritized” during the start of every “sprint” and the “overall design/architecture of the IT system” is “evolved” based on the new requirements.

There exists a plethora of documentation in terms of capturing the changing customer requirements, refining requirements based on new changes, adjusting project goals in during an agile project delivery etc. However, there exist only abysmal amount of documentation explaining software developers on “how to evolve the design and architecture of an IT-system adhering with best engineering pratices, while catering the requirement changes arising during the agile software process ?”. The main goal of this article is to fill this missing literature, in which we go through our example software project “eMystery” starting from simple requirements in the initial sprint, in which later diversify into larger complex requirements after the end of each sprint during the progression of the project. During each sprint we will discuss about “how to evolve the design of the system based on new requirement changes while adhering with best software engineering practices”.

Please note that before reading the rest of this article, it is not mandatory, yet recommended to skim through the articles;

  1. “Inside the Object-Oriented Toolbox — Mapping between Design & Implementation”, which provides an in-depth explanation of how you would map an “OO Design” presented in a “UML class diagram” to “Java” implementation code
  2. “Inside the Object-Oriented Toolbox — Avoiding bad design with SOLID Principles”, which describes on how to avoid bad practices in “software design” by applying “OO SOLID principles”

Project eMystery — Sprint 1

The requirements for our hypothetical “eMystery” project for “Sprint 1” are as below.

  • Need an encryption service, which is able to provide an encrypted file for a given plain text file.
  • Encryption is done using DES encryption

Let’s assume that, since the delivery deadline was tight and the requirements were too simple someone ended up implementing the below java-based solution which serves the purpose during “Sprint 1”.

Figure 2: EncryptionService after Sprint 1
Figure 3: EncryptionService consumer class (BizClass) after Sprint 1

Hence the simple design resulted after “Sprint 1” (according to the implementations presented in Figures 2 and 3); is as below;

Figure 4: Overall Design after Sprint 1

Although this solution serves the requirements for “Sprint 1”, can you identify the design issues behind it?

The EncryptionService class violates “Single Responsibility Principle (SRP)”. EncryptionService handles 4 responsibilities as below. (the first one is obvious, for 2, 3 and 4 have a look at the 3 regions of code circled in “red” in Figure 2)

  1. Exposing encryption as a service
  2. Handling data retrieval based on different IO sources
  3. Handling encryption based on different IO targets
  4. Handling encryption based on different algorithms

This design and implementation can be left as it is, if it is the end of the project and there are no more requirements to modify this module in future.

However the real problems arise due to the above design issue, when new requirements get added during “Sprint 2”.

Project eMystery — Sprint 2

“eMystery” project’s requirement changes for “Sprint 2” are as below.

  • Our encryption service, must support both DES and AES encryption algorithms

However, since we have to live with existing code from “Sprint 1”, this section is going to discuss 2 possible solutions that could cater the above new requirement within our existing implementation during “Sprint 2”.

  1. Survival Solution
  2. Best Practices Solution

Sprint 2 — Survival Solution

Figure 5 — Sprint 2, Survival Solution for the EncryptionService

We call this as the “Survival Solution”, since it requires only several minor modifications on the code we have from “Sprint 1” with “a very little effort” (see Figures 5 and 6), still leaving the same design from “Sprint 1” (as in Figure 4).

Figure 6 — Sprint2, Survival Solution for the BizClass

However from the “engineering practices” point of view, our EncryptionService class now violates “Open Close Principle (OCP)” in addition to violating “Single Responsibility Principle (SRP)” during “Sprint 1”. So we are going to see a solution which caters the requirement changes for “Sprint 2” and adhere with “engineering practices” the same time.

Sprint 2 — Best Practices Solution

We can move out the encryption part into a separate abstract class EncryptionAlgorithm as in Figure 7.

Figure 7 — Abstracting Content Encryption

Then we introduce the EncryptionAlgorithmFactory (see Figure 8), so that we can extend our design with any number of different encryption algorithms in future adhering with the “Open Close Principle (OCP)”.

Figure 8 — Introducing a Factory

Hence our new EncryptionService class will have the changes illustrated in Figure 9.

Figure 9 — EncryptionService using the Abstraction of EncryptionAlgorithm

As illustrated in Figure 9, introducing the EncryptionAlgorithm class has partially resolved the “Single Responsibility Principle (SRP)” related issues from “Sprint 1”, since the content encryption responsibility is now moved into the EncryptionAlgorithm class.

Figure 10 — Modified BizClass consuming EncryptionAlgorithmFactory and EncryptionService

Hence, with our “Best Practices” solution we’ve managed to achieve both; catering the requirement changes during “Sprint 2” and “evolve” the codebase into a better design adhering with software engineering practices (see Figure 11).

Figure 11 — Evolved Design after Sprint 2, adhering with Engineering Practices

Project eMystery — Sprint 3

For “Sprint 3”, the “eMystery” project has received the below requirement changes.

  • The sources or targets (i.e. destinations) in the encryption service can either be files in the local hard drive, or a data set remotely accessible via a webservice.
Figure 12 — Introducing IEntity to abstract Sources and Targets

So for the solution, we start by upgrading the String values representing source and destination in EncryptionService (from “Sprint 2”) to IEntity abstraction (see Figures 12 and 13). The main rationale behind this approach is to;

  1. Make sources and destinations independent from EncryptionService based on Dependency Inversion Principle (DIP) AND
  2. Divert away the remaining additional responsibilities of the EncryptionService class to make the overall design adhere with Single Responsibility Principle (SRP)
Figure 13 — IEntity Abstraction

Our BizClass will be modified as illustrated in Figure 14, in order to support the IEntity abstraction.

Figure 14 — BizClass consuming IEntity

Hence the overall “evolved” design of the whole system will be identical to what is illustrated in Figure 15.

Figure 15 — “Evolved” System Design after “Sprint 3”

Project eMystery — Sprint 4

Here comes the requirement changes just before commencing Sprint 4 for eMystery.

  • Wireless Connections will ONLY act as sources for the encryption service

The quickest solution would be to introduce the WirelessConnection class as in Figure 16.

Figure 16 — WirelessConnection class implementing IEntity

However this solution has a pitfall, since it violates “Interface Segregation Principle (ISP)”. The reason is that with the new requirement for WirelessConnection introduced in “Sprint 4”, all IEntity instance types DOES NOT need to act as both, sources and targets. Some IEntity types can only act as sources. Hence IEntity has become a “fatty” interface in our system.

Hence a better solution would be to split the “fatty” IEntity interface into 2 smaller interfaces; ISource and ITarget (see Figure 17).

Figure 17 — ISource and ITarget

We can write the WirelessConnection class, by only implementing from the ISource interface (see Figure 18).

Figure 18 — WirelessConnection implementing ISource

Hence our EncryptionService will have the new modifications as illustrated in Figure 19.

Figure 19 — EncryptionService with ISource and ITarget

Finally our BizClass will consume ISource and ITarget as illustrated in Figure 20.

Figure 20 — BizClass consuming ISource and ITarget

The overall design of the system at the end of “Sprint 4”, will be identical to Figure 21.

Figure 21 — “Evolved” System Design after “Sprint 4”

Conclusion

Hence with the nature of the requirement pipeline in an Agile-Driven environment, the features of a system could always “evolve”, with the continuously changing requirements. However, “careful refactoring” of the system design and implementation adhereing with “best engineering practices” similar to the simple walkthrough we underwent in this blog across 4 “sprints” will aid the developers to still maintain the quality of the software product whilst catering Agile-driven requirements during every sprint.

Interested in taking that initial step in improving your business by designing and developing a value-driven IT system with best Agile software practices? Contact Us.

--

--

Wimal Perera
Lexicon Digital

A Software Engineer with 12+ years of development experience; from frontend web to backend IT infrastructure. (https://www.linkedin.com/in/wimalperera/)