Rescuing a Test-Free Project with RSpec and Cucumber

Hi all,

I was recently brought onto a Rails 2.2.3 project which was
itself an emergency rescue of a spaghetti-coded PHP project (complete
with
hard-coded SQL statements!). Due to the fact that the code was already
in
production and has required fairly constant maintenance and feature
additions, the dev who switched everything over to Rails hasn’t written
a
single test. The eventual goal, the achievement of which is one of my
primary responsibilities on the project, is to have the project be
migrated
to Rails 3, fully spec’d at all levels and documented at the top level
with
Cucumber features, matching Rails conventions (e.g. the database table
for
the Shop model is currently ‘shops_master’ and will eventually be
‘shops’)
and with all vestiges of the original project completely removed
(there’s
still some PHP code running on the production site). My question is:
what’s
the best approach to get from here to there? Is it possible to do this
gradually while development continues on the current project, or is a
total
refresh going to be necessary? I’d much prefer a gradual approach
because
the other dev on the project is working full-time on adding features to
and
maintaining the current site and all of my responsibilities outside of
the
migration will be focused on adding features to the current site, so if
I
were to do a complete refresh any work from here on out would be
completely
duplicated. Additionally, the other dev on the project (who has much
more
general coding experience than I do) won’t be able to spare time to help
me
out with problems on a refresh the way he would if any gradual changes
were
implemented on the current project. The only problem with the gradual
approach is that I have no idea how to actually do it! Do I start with
unit-tests of the already-existing code and work my way out to features,
or
do I start with features describing things already implemented and work
my
way in? Do I try to convince the other dev to start outside-in with all
new
features now, or do I wait until I’ve done more with what’s already
there?
Are there any good resources out there for tasks like this? Also, if a
refresh IS necessary: what’s the best way to go about replicating the
functionality of an existing project?

tl;dr: Is it possible to save a
test-free project via gradual steps, or is a complete refresh necessary?
If
the former, how do I go about doing that? If the latter, how do I do it
in
a way that keeps overall functionality of the resulting project the same
as
the original?

Cheers,
Shea Levy

On Dec 11, 2010, at 11:47 AM, Shea Levy wrote:

Hi all,

I was recently brought onto a Rails 2.2.3 project which was itself an emergency
rescue of a spaghetti-coded PHP project (complete with hard-coded SQL
statements!). Due to the fact that the code was already in production and has
required fairly constant maintenance and feature additions, the dev who switched
everything over to Rails hasn’t written a single test. The eventual goal, the
achievement of which is one of my primary responsibilities on the project, is to
have the project be migrated to Rails 3, fully spec’d at all levels and documented
at the top level with Cucumber features, matching Rails conventions (e.g. the
database table for the Shop model is currently ‘shops_master’ and will eventually
be ‘shops’) and with all vestiges of the original project completely removed
(there’s still some PHP code running on the production site). My question is:
what’s the best approach to get from here to there? Is it possible to do this
gradually while development continues on the current project, or is a total
refresh going to be necessary? I’d much prefer a gradual approach because the
other dev on the project is working full-time on adding features to and
maintaining the current site and all of my responsibilities outside of the
migration will be focused on adding features to the current site, so if I were to
do a complete refresh any work from here on out would be completely duplicated.
Additionally, the other dev on the project (who has much more general coding
experience than I do) won’t be able to spare time to help me out with problems on
a refresh the way he would if any gradual changes were implemented on the current
project. The only problem with the gradual approach is that I have no idea how to
actually do it! Do I start with unit-tests of the already-existing code and work
my way out to features, or do I start with features describing things already
implemented and work my way in? Do I try to convince the other dev to start
outside-in with all new features now, or do I wait until I’ve done more with
what’s already there? Are there any good resources out there for tasks like this?
Also, if a refresh IS necessary: what’s the best way to go about replicating the
functionality of an existing project?

tl;dr: Is it possible to save a test-free project via gradual steps, or is a
complete refresh necessary? If the former, how do I go about doing that? If the
latter, how do I do it in a way that keeps overall functionality of the resulting
project the same as the original?

Cheers,
Shea Levy

There’s a great book by Michael Feathers called “Working Effectively
with Legacy Code,” in which he defines legacy code as code without
tests. The examples are mostly in C++ and Java, but many of the concepts
are technology-independent.

The short version is: do it gradually. Start with a “characterization”
test (generally end to end tests, probably using Cucumber) that
describes an existing feature and covers the parts of the code you’re
about to change for a new feature. Feathers’ book provides strategies
for figuring out what to test in characterization tests.

Once you’ve got the coverage you think you need to get you safely
through a feature you’re about to write, then drive it out using
whatever process you would normally use on a well tested app.

If you do this consistently as you add new features, you’ll build up a
meaningful suite pretty quickly.

Make sense?

Cheers,
David

On first thought what I would do since you have the project running in
production is to write all your Features and get them to pass on the
existing production application. That gives you a reality test on each
piece
you change as you rebuilt. You have an additional challenge being in
Rails 2
and wanting to go to Rails 3. I think if you want to do it gradually,
start
with the Rails 2 project, write all Features and get them passing and
then
rework the entire project gradually to completion in Rails 2. Then
once
all this is done and everything is passing (Features/Functional/Unit
tests),
then migrate it to Rails 3. Of course this might be harder and a bit
more
time consuming than a full rewrite in Rails 3 but you will gain the
benefit
of shadowing a functional in-production project.

Hi,

I would highly recommend reading “Working with Legacy Code” by Michael
Feathers. It’s full of advice on how to lock down legacy code and save
it gradually. The examples are mostly (all?) in Java, so the actual
steps would be different, but I think it could give you an idea of
how/where to start.

Katrina