Simon Brown, a developer, architect and author, considers that it takes a lot more than just good code to create a successful project. In his presentation, Good Code Isn’t Enough, Brown goes through all the elements necessary for a project’s success, from upfront design to operation documentation.
Brown considers that good code is a good place to start, but to succeed one needs to know what is to be built, what is released and that it works.
To know what to build, one needs a set of requirements. After the requirements are collected, a “big picture” is to be drawn, the software architecture which represents the current understanding of the product to be built. Then the big problem needs to be decomposed into smaller solutions showing the components, the interactions between them, and the services used. Then, estimates can be made on how much it will take to implement the solution. All of that, from determining the requirements to making estimates should take only 1-2 days, according to Brown. This is not a one man’s job, this should be done collaboratively with all those directly involved in order to cross-pollinate ideas.
If done properly, a lightweight software architecture introduces structure – “what the components are and how they talk to one another” - and guidelines – which “comes from documenting patterns, templates and principles” - into the project, leading to consistency – by having a “standard approach to solving common problems” - and clarity – one knows where he is heading because he has seen the “big picture” now. As a counterexample, Brown mentions a project using three persistence solutions: Spring, EJB and Hibernate. That was because nobody decided upfront what persistence framework was to be used.
The next step is prioritizing. Unless it is a small project, one should not attempt to build and release the whole project in one step, but rather decide what is the most important and do that first. That is done by refining and challenging the scope, deciding to implement a subset of the requirements, enough to fulfill a working part of the initial vision.
Next comes tracking progress. There are different ways to track progress like spreadsheets or Kanban boards. Brown notes that these are used to know the progress done so far during the iteration, but they do not actually track software released.
Does the architecture work? Everything is useless if the resulting solution does not work as desired. Brown considers that for a solution to work it has to satisfy the following requisites:
- it satisfies the architectural drivers: the functional and non-functional requirements, the environmental constrains and the principles adopted
- it provides a solid foundation for the code, one that one can built upon
- it provides a platform for solving the initial business problem the solution tries to address
Build a prototype. No matter how great an architecture is, not matter how beautiful the code looks, no one really knows if the system will perform satisfactorily and if it will scale. A prototype is what is needed. The prototype is a proof of concept containing some vertical slices through the system, touching the requirements and the foundations, just enough to simulate actual functioning and to put the entire system under pressure through load testing. The code used for the prototype can be used later for production or it can be thrown away.
Load testing is done by “simulating multiple users with a typical usage profile, and preferably with an environment as near to production as possible”. The prototype and load testing is to be done in the early phases of the project in order to validate the architecture.
Using a source code control is an important step in building a solution by providing: backup of the source code, a log for the changes made, a way to revert to a different version, simplified parallel development.
Automated test is another required piece of the puzzle. Is the new code just added breaking anything? Changes done to a portion of the project can have negative effects on others. To limit the possible damage done by changes, unit tests and integration tests are mandatory, otherwise the entire functionality of the system needs to be hand-tested after each change. How much to test? Brown considers that 100% testing coverage is hard to achieve, quite impractical, suggesting 80%, covering the most important pieces of the code.
Automated Build. After the code is tested, it needs to be built and deployed on the target machine(s). But many times the build does not work on the other machine, and a build script is necessary to make sure the system can be properly built on any target machine.
Yet another useful step, according to Brown, is using Continuous Integration. A continuous integration server will automatically retrieve source code from the repository, compile, test, package and install it, signaling any errors that appear during the process. This helps making sure the code builds correctly and any problem is spotted early.
Automation of testing and building introduces Consistency and Repeatability. Automation is even more useful when doing parallel development, working on various branches of the code.
Externalize configuration information. System configuration information depends on the environment and it is advisable to be kept and maintained in one location outside of the code.
The application version should be contained somewhere: in meta-data, in a diagnosis page, in the log file, to make sure one can tell what version of the program he is looking at.
The last step, Operational Documentation. If the development team is not the one supporting the software, then some operational documentation is necessary, containing information on how the system is to be used, monitored and managed, and how problems are to be diagnosed.
All these elements that contribute to creating a product with chances to success are contained in the following slide: