down migrations could be stored in db?

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.

Feedback?

Steve

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.

-- Chad

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.

-- Chad

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.

-Ryan

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.

Amen

Great idea, thanks...

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.