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:

...also some anachronistic thinking. Example: One project had 3 persistence mechanisms, ostensibly "because nobody decided upfront what persistence framework was to be use." My take on the story is that the team didn't decide on a persistence framework at the last responsible moment for deciding on a persistence framework. Depending on circumstances, the LRM might indeed be "upfront," but not always.

Still, the article offers mostly good advice, IMHO. Let me question an assumption embedded in the title, though: Is good code even necessary for a successful project?

Hi Dave,yes, good code is necessary for a project's success. The title is the way it is because it wants to draw the attention on something: the hyperfocus on writing code, and not considering many other important issues, something which happens more often than some would guess according to Simon Brown.

Well, there can be a successful project at first release even if the code is not so good. But problems will surface later when the code needs to be extended and maintained. A good code will facilitate that, while bad code will make things really hard. That does not necessarily mean a total failure of the "bad code" project, but it won't be a great success either, unless they do a major overhaul. Now, it is pretty hard to measure what good code and success are since we don't have a scale for that, but I think we all get the idea.

I've been on projects that got canceled, been asked to help out on others that were struggling. All I can say is that I think the reasons for the issues were rarely technical. They were more often a case of people failing to understand the real scope of work, failing to listen to one another, and committing to too much too early.

OTOH, bad code rarely causes delivery failure on shorter term projects in my experience... it's the post delivery phase that you end up paying that price.

It's the famous technical debt. It increases over time and eventually can made a product very hard or impossible to maintain. The tricky thing is that it's impossible to measure effectively. Even good code will eventually become "spaghetti" over time if not refactored.

It is a big problem indeed. I met a lot of so called Agile devs who happily code new things, but struggle to seriously change already existing code or delete dead one ("Oh man, it was so hard to write...", "It works fine, let's just hack it a bit to keep it working" or "We still might need it some day"). Then project degrades and turns into Big Ball of Mud no one really wants to touch... I think merciless refactoring and constant domain adjustment are main activities to keep the project "alive". But yeah, it's a hard work.

Is your profile up-to-date? Please take a moment to review and update.

Email Address

Note: If updating/changing your email, a validation request will be sent

Company name:

Keep current company name

Update Company name to:

Company role:

Keep current company role

Update company role to:

Company size:

Keep current company Size

Update company size to:

Country/Zone:

Keep current country/zone

Update country/zone to:

State/Province/Region:

Keep current state/province/region

Update state/province/region to:

Subscribe to our newsletter?

Subscribe to our architect newsletter?

Subscribe to our industry email notices?

You will be sent an email to validate the new email address. This pop-up will close itself in a few moments.

We notice you're using an ad blocker

We understand why you use ad blockers. However to keep InfoQ free we need your support. InfoQ will not provide your data to third parties without individual opt-in consent. We only work with advertisers relevant to our readers. Please consider whitelisting us.