Agile has grown
and evolved from a very simple developer centric process defined by Extreme
Programming to a complex product brand that enterprises are using to bring more
resiliency to governance programs, enterprise architecture initiatives, and
application portfolio management efforts. But at its roots, there remains a key
fundamental aspect that defines the essence of agility on the software
development project. Continuous Integration is a strategy where software is
integrated and built continuously, or at least as frequently as is feasibly
possible. Many teams have adopted a continuous integration strategy, yet do not
fully realize all the benefits that continuous integration might bring to the
development effort. This article discusses the subtle though significant ways
that continuous integration can be leveraged -from helping to align IT with the
business to enforcing architectural constraints - and shows that this
fundamental aspect of agility is the defining and necessary element of a truly
agile development experience.
Closing
the Loop
At the center of
a continuous integration strategy is an automated and repeatable build. In Activating the Lifecycle, I talked about the aspects defining a robust build
process, and pointed out some of the benefits of an automated and repeatable
build. But an automated and repeatable build does not guarantee that a software
development team will realize the advantages of continuous integration. Having
worked with numerous software teams who have setup an automated and repeatable
build, I've discovered that the most successful teams use continuous
integration to close the feedback loop.
Advertisement
For a moment,
visualize the macro software lifecycle. Traditional teams begin by analyzing the problem and gathering requirements, designing a solution, constructing the software, executing tests, and finally, deploying the completed software. Of course, iterative development aims to perform each of these steps within iterations, possibly a four to six week period. Certainly it's logical that if we perform a slice of the entire software lifecycle in less time, we'll generate important feedback that can be used to plan future iterations. This is the greatest benefit of iterative development, and most experienced development teams recognize its value. Like a traditional software lifecycle, iterations
tend to go well the first few days. But as the iteration deadline approaches, the team frequently falls behind schedule, often creating a backlog of items to be addressed in subsequent iterations. Now take this notion one step further. Continuous integration aims to perform the entire software lifecycle each time the automated and repeatable build is performed. In other words, each build represents a complete pass through the software development lifecycle. Is this possible? Is it entirely realistic to believe that a team can perform the entire lifecycle each time the software is built? Hourly? Possibly even more frequently? Doubtful. However, a sound continuous integration strategy creates the ability for a software team to perform any part of the lifecycle any time they desire or need. This very notion, the ability to perform any part of the software lifecycle at any time, is the single greatest benefit that teams practicing a successful approach to continuous integration understand. It is no surprise, then, that teams successful with continuous integration have mastered knowing when to perform software lifecycle activities to maximize the necessary feedback at the appropriate moment in time. These teams have closed the feedback loop, and left little room open for high risk, long-term, unsuspecting problems to surface.
The Right Stuff
Of course, if software development was as simple as implementing a continuous integration strategy, we'd all have done it long ago. Unfortunately, while some teams find great success with continuous integration, others do not. Knowing when to perform various lifecycle activities can be amazingly difficult. As a general rule, any activity performed later in the software lifecycle that can be moved toward the beginning of a project helps you gauge your progress. Essentially, you can view continuous integration as a process pattern. Like all other patterns, there is a common underpinning that forms the foundation of continuous integration (that being the build), but the overall strategy must be tailored to fit your context (i.e., your team, your project, and your technology). Young developers learn early that simply because the application works on their machine doesn't mean that it works on other machines. Experienced continuous integration practitioners recognize that a successful build only proves that the software passes the compiler and the automated test suite, but does not mean that the application does what clients need. One oft-cited benefit of continuous integration is that teams avoid significant problems, such as compile errors, when attempting to integrate code. While true, this is only the beginning, as there are other critical activities that can dramatically improve the advantages of a successful continuous integration strategy.
Communicate Results
When a build fails, whether due to a compiler error or test failure, all team members should be notified. Upon failure, it must become the highest priority of all team members to fix the problem. Other significant events should also be communicated, such as tests that execute slowly and successful deployments to various tiers.
I've long held the belief that the source code is the most important artifact created by the software development team, as the source code is the only artifact used to create the executable version of the product. In The Value of executable Artifacts, I discussed a number of ways that teams can extend the build to provide feedback to the development team. For many teams, however, their continuous integration strategy stops after the source is compiled and tests are executed. Except for iteration milestones where a specific build is tagged for deployment, most times the compiled settles into the dust bin and is never used for anything useful. While compiling the application and executing all unit tests provides some advantages to the software developer crafting the application, teams that fail to deploy the build are missing out on important feedback. Bundling the application and deploying it to a simulated production environment completes the development lifecycle and opens the possibility to perform other important lifecycle activities such as:
Making the
Application Accessible. The ability to build and deploy the software system
means the team always has a functional product, and teams should take advantage
of this fact. If you're developing a rich client application, be sure it is
bundled in the same manner as if it were a true production release and allows
team members to download and install the application. If it is a web
application, publish the URL so all project team members have access. Encourage
folks to experience the application. Along the way, always encourage customer
feedback.
Creating Dashboard.
With each build, update a project dashboard. Typically, the dashboard is a web
application containing information on the most recent build. Such information
might include test coverage reports, static analysis reports such as JavaNCSS,
auto-generated documentation such as JavaDoc, and visual representations such
as those produced by JDepend and JarAnalyzer. Use this
information as part of corporate architecture, design, and code reviews to
somewhat objectively analyze the quality of your source code.
Testing the
application. From the moment you have a functional product that is accessible,
you can develop and execute test suites that include load tests, performance
tests, usability tests, failover tests, and acceptance tests. Getting quality
assurance involved early in the project lifecycle ensures that continuous
testing takes place and allows the team to grow a very robust suite of tests.
Continuous regression testing, beginning very early in the project, ensures that
any problem encountered is only as old as the most recent successful run of the
test suite. As the test strategy evolves, project management can begin tracking
project status through the growth of successful features that have been tested
over time.
Enforcing
architecture. By structuring your build scripts so that lower level modules
with fewer dependencies are built first, followed by those modules with
dependencies on perviously compiled components, you can enforce architectural
integrity. Through automation, you can track and manage component versions. If
undesirable architectural qualities begin to surface, you will be able to
identify them.
Proving requirements.
A great way to garner feedback from your customers is to show them the system,
and allow them to experiment by simulating their work. Frequent system demos
involving customers and developers are a great supplement to traditional
requirements elicitation sessions. Showing the system to both developers and
clients helps facilitate discussion and iron out any challenges associated with
especially tricky or complex areas of the application. Especially as some folks
might drown themselves in the details of a specific use case or subsystem,
system demonstrations ensures that everyone maintains a consistent perspective
on application growth.
Tracking Progress.
The consistency and frequency of your approach to continuous integration allows
you to identify and manage trends throughout the development lifecycle.
Maintaining a history of past builds on a project dashboard allows project
managers and developers to track the ration of successful to unsuccessful
builds, identifying potential quality issues. Trends in coverage reports and
analysis tools helps developers manage growth successfully. Recognizing growth
in total features tested can help establish more reliable development velocity.
Sticking with
what works. Teams with a successful continuous integration strategy need not
change their process after their first production deliverable. Application
maintenance efforts that are of short duration with limited scope fit well into
the existing approach. Because teams have been focusing on managing change
throughout the software lifecycle, change requests are an inherent part of
their work.
Conclusion
I've found that
any activity delayed until late in the software lifecycle is a breeding ground
for risk. Fail to execute performance tests frequently and it's likely that the
team will experience performance problems with the application. Fail to deploy
the application to an environment simulating production and it's likely that the
team will experience deployment issues. Worse yet, failing to integrate the
system frequently often results in integration issues.
Teams leveraging
continuous integration prove the full lifecycle as frequently as possible. They
develop, test, integrate, and deploy every time they build. The more frequently
you perform all software lifecycle activities, the less likely it is that
unforeseen high risk issues will surface unexpectedly. A sound continuous
integration strategy can be leverage to help deal with many pain points
throughout the software lifecycle because the project team always has a
functional product at a given moment in time. Successful teams use that product
to close the feedback loop to ensure that unexpected issues don't surface that
can kill a project.
About the Author Kirk
Knoernschild is Senior Technology Strategist at TeamSoft, where he leads based on his firm
belief in the pragmatic use of technology. In addition to his work on large
development projects, Kirk shares his experiences through courseware
development, teaching, writing, and speaking at seminars and conferences. Kirk
has provided training to thousands of software professionals, teaching courses
on UML, Java J2EE technology, object-oriented development, component based
development, software architecture, and software process.
Comments (3)
kirkk: ...
Thank you for the feedback...each of you.
Usually, when I create a separate branch, I setup a separate build for that branch. So if I have a Release 3 branch for my next release along with the HEAD stream for future releases, I'll have two separate build processes. Usually, it's the same subset of developers working on the different streams. But whether the build fails/succeeds for Release 3 or HEAD, I like the notification to go out to the entire team. I've always felt it offers a bit of comfort across/within the team(s) as they see those build results pushed out.
1
November 21, 2007
Chris Boran: ...
Just to counter your argument Eivind ;-) I work on a team of 160 engineers and we do follow the 'everyone knows when the build breaks' philosophy and it does have some benefits.
Firstly, it serves as a heads up to anyone who might be contimplating either a big checkin or a merge point - it helps information flow around the organisation so that no one does anything to make the situation worse.
Secondly, it helps keep developers from getting lazy out of pride - no one wants everyone to know that they broke the build because of carelessness (and let's face it, build breakages usually are born out of careless behaviour), and having a public record stated as a matter of fact, I find, makes developers slow down and pay attention to doing it right.
All that you need to do to make this work is make sure that people know to keep focus - as part of your breakage report, include the names of the people who have checked in since the last successful build, and then those people are the ones who will spend time seriously looking into it, while the rest of the team can be aware, but not waste any time on it.
My opinion is that lack of communication is a far greater sin and time waster than too much communication.
Chris
2
November 16, 2007
Eivind Hagen: ...
Great article, Kirk! Lots of good advice in there.
I do take issue with one of your recommendations though:
"When a build fails, whether due to a compiler error or test failure, all team members should be notified"
This seems only practical on a very small team. If you have 30 engineers working on a project (in several Scrum teams) then it would be a total waste of their time to be notified each time someone else brakes the build. Large teams will typically work in multiple branches, so the failure broadcast should at least be limited to the people in that particular branch. Even then, unless the developers actually need the latest-and-greates version of the code, they may not need to be involved in fixing it.
I suggest that the Lead Developer (or some other 'Build Captain') be the point person to handle the situation, and possibly recruit the help of others depending on the nature of the breakage.