I work on a team of about 5 committers Rails project in a git repo. We often run in to issues with migrations that have run in one branch and not another, or migrations that have run on deploy that aren't automatically ran down when we roll back our production server to an older version.
I think the problem could potentially be resolved if the database stored the code to run the down method of a migration along with the list of migrations that have run. On rake db:migrate, if a migration is missing, it could prompt (or accept an option) to automatically run the down method for the missing migrations.
I'd be happy to implement this, the only potential issue I see is the down method in the db getting out of date with the code in the migration file, but maybe that's not much of an issue.
I think the problem could potentially be resolved if the database
stored the code to run the down method of a migration along with the
list of migrations that have run. On rake db:migrate, if a migration
is missing, it could prompt (or accept an option) to automatically run
the down method for the missing migrations.
That'd work only for cases where your down migrations *only* change
the schema, they don't touch models or any other ruby code. Otherwise
there's no guarantee that those models are available when the system
tries to downgrade.
Why can't you run the down migration before rolling back the code?
That's the only reliable way you'll be able to do this.
Doesn't this same problem exist always - whether you are going down or
up, or store your migrations in files or the database? Some would
consider it an antipattern to put any models or app classes in your
migrations.
I'm still not clear on the original poster's root problem and
solution, but I don't think the possibility of outdated non-schema
migration code is a good reason to reject the idea.
I see sublime elegance in having the DB store the DDL to revert
migrations. I haven't worked with large teams in years, but I see the
dilemma Steven is addressing:
The common DB is at Rev N
User (or branch) A migrates common DB up to Rev N + 1
User (or branch) B is "checked out" and expects common DB to be at Rev
N.
Result: User/branch B is stuck: code to rollback the DB to Rev N
exists only in User/branch A.
Sorry if that was obvious to most, but I needed to clarify at least
one scenario where this technique could be useful.
In this case, I would say that user (or branch) B should not be
running outdated code against the 'common' database at Rev N +1. The
+1 migration probably had some associated code changes to support the
schema changes, and if user/branch B does not have that code yet, they
shouldn't be running against that database. Either use a separate
database, or cherry-pick the migration and associated changes from
branch A. I think the concept of a "common" DB is the root problem
here. Each developer should have their own DB in development, and in
demo/staging/production, you should only be running a single branch
(preferably a tag of a green CI build) against the DB at any given
time.
Since database.yml gets ERB’d, I’ve seen a trick where the database name is lazy evaluated w/ the output from a git branch command. This way each branch has a seperate DB. There is a bit of manual work involved to make sure you create the database each time you create a new branch, but that’s solvable with a shell alias (or rake task) that creates the branch and DB for you.
Doesn't this same problem exist always - whether you are going down or
up, or store your migrations in files or the database? Some would
consider it an antipattern to put any models or app classes in your
migrations.
Some would be wrong if they considered that an anti pattern ;). The
vast bulk of migrations in my projects are model-dependent for things
like cleaning up a data problem in production, initialising counter
fields based on non-sql-calculatable fields (e.g. API calls)
If you store the migrations in your version control system you can
recover the state of the migrations at any point in time. For a
particularly tricky down migration you can always step back revisiion
by revision. The database would have no such history and could never
provide the same level of fidelity or reliability that your SCM can.
I'm still not clear on the original poster's root problem and
solution, but I don't think the possibility of outdated non-schema
migration code is a good reason to reject the idea.
The specific case mentioned was rolling back when the checkout is at
an older version and doesn't have the down migration. The solution
there is to rollback from the original revision.
If there's another case I'd be happy to re-evaluate but the power of
migrations is in their simplicity they're not some darcs-ish patch
algebra for DDL, they're little scripts that you run in order in which
you can do whatever you want.
Doesn't this same problem exist always - whether you are going down or
up, or store your migrations in files or the database? Some would
consider it an antipattern to put any models or app classes in your
migrations.
Some would be wrong if they considered that an anti pattern ;). The
vast bulk of migrations in my projects are model-dependent for things
like cleaning up a data problem in production, initialising counter
fields based on non-sql-calculatable fields (e.g. API calls)
My normal practice when writing migrations which depend on model code
is to put a migration specific model clase in the migration file
itself, scoped inside the migration. The model mimics a subset of the
one in app/models including only necessary attribute declarations and
methods.
If you store the migrations in your version control system you can
recover the state of the migrations at any point in time. For a
particularly tricky down migration you can always step back revisiion
by revision. The database would have no such history and could never
provide the same level of fidelity or reliability that your SCM can.
Absolutely true. And the technique I described decouples the running
of migrations from the current state of the models, which facilitates
recovering the state of the migrations without requiring incremental
steps of get a revision, run migrations, get the next revision run
migrations... if that makes sense.
I'm still not clear on the original poster's root problem and
solution, but I don't think the possibility of outdated non-schema
migration code is a good reason to reject the idea.
The specific case mentioned was rolling back when the checkout is at
an older version and doesn't have the down migration. The solution
there is to rollback from the original revision.
I have to say that I never understood the proposed solution, frankly
it scared the bejesus out of me, but maybe that's just me.
If there's another case I'd be happy to re-evaluate but the power of
migrations is in their simplicity they're not some darcs-ish patch
algebra for DDL, they're little scripts that you run in order in which
you can do whatever you want.
This is also great way to define "don't exist anymore" associations - so you can rely on them being there for that migration whether they exist or not in your actual models.
Just make sure to heed Rick's advice and put it inside your migration, otherwise dark spirits will come and steal your soul while you sleep.