Preamble - Crafting enterprise web applications
In an era of agile delivery, small pieces of functionality are constantly shipped to users. The software industry began favoring this approach as it helps minimize risk and instead maximizes user engagement and satisfaction.
Even with modern delivery methodologies, some risk is still inevitable. Complexity is one such risk, and can become a major concern for mature applications. Regardless of which system architectures an application may follow, over time, many small pieces of functionality can build up into a large and daunting codebase that requires several teams to oversee.
The opportunities to implement new, cleanly-designed features become rarer the longer an application is in production. Instead, existing features are more likely to be tweaked, bug-fixed or extended. Successful applications - and the features that comprise them - spend the majority of their lifecycle under maintenance.
Maintaining a complex application requires great discipline. It is far too easy for teams to get overwhelmed and spend their time clashing with codebase and colleagues instead of delivering value to users. Mitigating this risk involves many different approaches, covering areas of standards, patterns, technology choices and tooling, amongst others.
Errors are best caught as early as possible in the software delivery lifecycle. It is far quicker and cheaper to fix an error that has just been introduced in a single development stream than it is once the error has progressed through the entire delivery pipeline and is live in production where users may get negatively impacted.
A good way of catching errors early is favoring strong typing in the application development phase. Logical errors caused by mismatched data types can be avoided if type information is made explicit in application code. Compilers and static type checkers can validate against the type information and fail a build when such a type mismatch occurs. Software can only progress past an individual developer’s workspace to the rest of the delivery pipeline once all such errors are resolved.
When using the Dojo CLI to scaffold applications, a TypeScript compilation phase is included by default in the application build process. Developers can simply start writing type-safe application code from the outset.
Modularization - single responsibility principle
A component should ideally be small enough for it to only implement a single responsibility. The simpler and more encapsulated a component is, the easier it becomes to understand and maintain between any number of developers over long periods of time. Complex applications with large codebases are built up through combinations of many such smaller, more well-understood components.
Isolating responsibilities within individual components has many benefits when trying to reduce complexity:
- Scope is limited. Assuming a component maintains a consistent API, internal changes can be made without affecting external users of the component. Conversely, details of the component are kept internal to its definition module, meaning its definition will not conflict with that of other components that may overlap certain naming conventions.
- Testing requirements are simplified, as unit tests only need to focus on a single responsibility rather than exponential combinations of application flows through multiple conditionals.
- Components can be reused in multiple locations, avoiding repetition. Bug fixes need only be made to a single component instead of several independent instances.
For web applications, isolation comes with additional benefits to end users. Applications can be partitioned into multiple layers, allowing users to load only the layer they are interested in at a given point in time. This reduces resource size and associated network transfer requirements, resulting in shorter load times for users before they can become productive.