# How do you do database migration/evolution?

**URL:** https://clojureverse.org/t/how-do-you-do-database-migration-evolution/2005
**Category:** Watercooler
**Tags:** clojure
**Created:** [April 20, 2018, 3:36pm UTC](https://clojureverse.org/t/how-do-you-do-database-migration-evolution/2005 "2018-04-20T15:36:37Z")
**Posts on this page:** 8
**Page:** 1

<div class="post-metadata">

### Author: ![Webdev\_Tory](https://clojureverse.org/user_avatar/clojureverse.org/webdev_tory/32/3781_2.png) [@Webdev\_Tory](https://clojureverse.org/u/Webdev_Tory)
#### Post date: [April 20, 2018, 3:36pm UTC](https://clojureverse.org/t/how-do-you-do-database-migration-evolution/2005/1 "2018-04-20T15:36:37Z")

</div>

Having started my road down Clojure webdev with Luminus, I’ve defaulted into using Migratus for database migration. I’ve had no real problems with this. But as I’ve talked to Play developers, I find find them bemoaning the absence of automated database evolutions in our Clojure projects. This has me wondering, what are your solutions for managing databases for your projects, particularly for team-based projects?

[https://www.playframework.com/documentation/2.5.x/Evolutions](https://www.playframework.com/documentation/2.5.x/Evolutions)

---

<div class="post-metadata">

### Author: ![pesterhazy](https://clojureverse.org/user_avatar/clojureverse.org/pesterhazy/32/395_2.png) [@pesterhazy](https://clojureverse.org/u/pesterhazy)
#### Post date: [April 20, 2018, 5:07pm UTC](https://clojureverse.org/t/how-do-you-do-database-migration-evolution/2005/2 "2018-04-20T17:07:50Z")

</div>

A minimalist approach is to store the migrations as .sql files in a folder, then run them via a command line client like `psql` or from your Clojure code via JDBC.

For Postgres, I’ve come up with a [minimalist framework](https://gist.github.com/pesterhazy/9f7c0a7a9edd002759779c1732e0ac43). It’s so simple that it doesn’t really deserve the name “framework”. In a nutshell the `idempotent` function ensures that a particular migration (say, CREATE TABLE) is run only once. Subsequent runs are NOOPs. Completed migrations are stored in the `migrations` table.

Advantages over “real” migration tools like [flyway](https://flywaydb.org/):

- it’s fast if there’s nothing to do so you can easily run all migrations whenever your JVM process starts
- you know exactly what’s going on
- it’s all just SQL so the sql snippets can include data migrations (`UPDATE foo ...`) as well as schema migrations (`ALTER TABLE foo ...`)

Limitations include lack of support for rolling back migrations. But if your use case is like mine, you may not need that functionality (and appreciate the fact that the code fits on a postcard).

---

<div class="post-metadata">

### Author: ![orestis](https://clojureverse.org/user_avatar/clojureverse.org/orestis/32/279_2.png) [@orestis](https://clojureverse.org/u/orestis)
#### Post date: [April 20, 2018, 6:28pm UTC](https://clojureverse.org/t/how-do-you-do-database-migration-evolution/2005/3 "2018-04-20T18:28:15Z")

</div>

I can’t remember which talk was it, but it strongly suggested that reverse migrations are a huge anti-pattern — you can accidentally lose data, and there’s no good reason to use them.

---

<div class="post-metadata">

### Author: ![swlkr](https://clojureverse.org/user_avatar/clojureverse.org/swlkr/32/1624_2.png) [@swlkr](https://clojureverse.org/u/swlkr)
#### Post date: [April 21, 2018, 7:56pm UTC](https://clojureverse.org/t/how-do-you-do-database-migration-evolution/2005/4 "2018-04-21T19:56:49Z")

</div>

I use reverse migrations in development only. When switching branches or when I make a mistake and want to easily drop a few tables/columns.

But yeah on a live server, I never rollback migrations.

---

<div class="post-metadata">

### Author: ![myguidingstar](https://clojureverse.org/user_avatar/clojureverse.org/myguidingstar/32/6150_2.png) [@myguidingstar](https://clojureverse.org/u/myguidingstar)
#### Post date: [April 22, 2018, 7:43am UTC](https://clojureverse.org/t/how-do-you-do-database-migration-evolution/2005/5 "2018-04-22T07:43:41Z")

</div>

I really I the flow of Duct framework which use ragtime for migration, configured with good defaults:

> <https://github.com/duct-framework/docs/blob/master/GUIDE.rst>

  
but you may like joplin, which is built on top of ragtime itself  

> **[juxt/joplin](https://github.com/juxt/joplin)**
>
> joplin - Flexible datastore migration and seeding for Clojure projects

---

<div class="post-metadata">

### Author: ![Webdev\_Tory](https://clojureverse.org/user_avatar/clojureverse.org/webdev_tory/32/3781_2.png) [@Webdev\_Tory](https://clojureverse.org/u/Webdev_Tory)
#### Post date: [April 24, 2018, 7:05pm UTC](https://clojureverse.org/t/how-do-you-do-database-migration-evolution/2005/6 "2018-04-24T19:05:52Z")

</div>

What are reverse migrations?

---

<div class="post-metadata">

### Author: ![orestis](https://clojureverse.org/user_avatar/clojureverse.org/orestis/32/279_2.png) [@orestis](https://clojureverse.org/u/orestis)
#### Post date: [April 24, 2018, 7:41pm UTC](https://clojureverse.org/t/how-do-you-do-database-migration-evolution/2005/7 "2018-04-24T19:41:38Z")

</div>

Usually when ORM tools auto-generate migrations (by comparing the current vs wanted schema), they will generate a “forward” migration script that will take you to the desired schema, and a “backwards” migration script that will get you back to the current state.

This opens up some very bad fat-fingerings if you accidentally run it on a production database, as it may drop columns, tables etc that may already have data in…

---

<div class="post-metadata">

### Author: ![system](https://clojureverse.org/uploads/default/original/2X/5/51079bf9e4b7d9466242c06cf1e43b9f8bd6da14.png) [@system](https://clojureverse.org/u/system)
#### Post date: [October 24, 2018, 7:50am UTC](https://clojureverse.org/t/how-do-you-do-database-migration-evolution/2005/8 "2018-10-24T07:50:19Z")

</div>

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.
