Learning Laravel while building a real beekeeping app taught me these lessons the hard way.
Last week, I spent 3 frustrating days debugging migration issues while building BeeNote, my beekeeping management app. What should have been a simple database setup turned into a PostgreSQL nightmare.
Here are the 5 mistakes that cost me the most time – and the solutions that finally worked.
1. The Duplicate Index Trap 🪤
The Mistake:
Schema::create('photos', function (Blueprint $table) {
$table->morphs('photoable');
$table->index(['photoable_type', 'photoable_id']); // DUPLICATE!
});
The Error:
SQLSTATE[42P07]: Duplicate table: relation "photos_photoable_type_photoable_id_index" already exists
The Fix:
Laravel’s morphs()
method automatically creates the index. Don’t add it manually.
Schema::create('photos', function (Blueprint $table) {
$table->morphs('photoable'); // Index created automatically
$table->index('ordre'); // Only additional indexes
});
Time saved: 4 hours of PostgreSQL debugging.
2. Wrong Data Types for GPS Coordinates 🗺️
The Mistake:
$table->float('latitude'); // Precision loss!
$table->float('longitude'); // Your beehives will "move" 10 meters
The Fix:
$table->decimal('latitude', 10, 8); // 47.12345678 - exact precision
$table->decimal('longitude', 11, 8); // -1.23456789 - no drift
When you’re tracking beehive locations, 10-meter precision errors aren’t acceptable.
3. Forgetting Business Logic in ENUMs 📝
The Mistake:
$table->string('queen_color'); // Free text = chaos
// Users input: "blue", "Blue", "BLUE", "bleu", "azul"...
The Fix:
$table->enum('couleur_marquage_reine', [
'blanc', 'jaune', 'rouge', 'vert', 'bleu'
])->nullable(); // International beekeeping color code
Real beekeeping uses standardized queen marking colors. Research your domain!
4. Performance Killer: Missing Composite Indexes 🐌
The Problem:
50ms query time on 10k+ records because I didn’t think about real-world usage patterns.
The Solution:
// Users frequently filter by: their tasks + status
$table->index(['user_id', 'statut']); // Composite index
// Query time: 50ms → 2ms
Think about your WHERE clauses before you have performance problems.
5. Wrong Cascade Strategy = Data Loss 💥
The Mistake:
$table->foreignId('visite_id')
->constrained()
->onDelete('cascade'); // DESTROYS history!
The Fix:
$table->foreignId('visite_id')
->constrained()
->onDelete('set null'); // Keeps history, breaks link
When a visit gets deleted, I want to keep the task history – just remove the connection.
The Real Learning: Domain Knowledge Matters
Building BeeNote taught me that migrations aren’t just technical – they tell the story of your business logic.
My visits table has 47 columns because beekeeping is complex:
- Weather conditions affect bee behavior
- Queen marking follows international standards
- Varroa mite counts determine treatment timing
- etc.
The technical complexity reflects the real-world complexity.
What’s Next?
These 5 mistakes were just the beginning. I documented the complete migration journey – including the full PostgreSQL schema, performance optimizations, and why I chose VILT stack over TALL stack for this project.
→ Read the full technical deep-dive here (warning: it’s detailed, includes all the code, and explains the beekeeping business logic behind each decision)
Currently building this in public as I transition from employee to freelance Laravel developer. Following my progress? Find me on X/Twitter @Llieudi.
What migration mistakes have cost you the most time? Drop them in the comments – let’s learn from each other’s pain! 😅
Top comments (0)