<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/a-stray-commit-buried-multiple-levels-deep-cost-me-months-auv5pc3lh" -->

---
title: A stray commit buried multiple levels deep cost me months
description: A developer shares a painful months-long refactoring experience caused by a stray db.commit() buried deep in helper methods, breaking transaction atomicity....
canonical: https://daily.dev/posts/a-stray-commit-buried-multiple-levels-deep-cost-me-months-auv5pc3lh
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: A stray commit buried multiple levels deep cost me months | daily.dev
og:description: A developer shares a painful months-long refactoring experience caused by a stray db.commit() buried deep in helper methods, breaking transaction atomicity....
og:url: https://daily.dev/posts/a-stray-commit-buried-multiple-levels-deep-cost-me-months-auv5pc3lh
og:image: https://api.daily.dev/og/posts/AUV5PC3Lh.png
og:image:alt: A stray commit buried multiple levels deep cost me months
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# A stray commit buried multiple levels deep cost me months

**[Hacker News](https://daily.dev/sources/hn)** · 6 min read · 0 upvotes · 1 comments

## Summary

A developer shares a painful months-long refactoring experience caused by a stray db.commit() buried deep in helper methods, breaking transaction atomicity. The post lays out concrete rules: the DB abstraction layer must own all commits and transactions, DB models must never leak outside that layer, and atomic multi-writes should live in a single function. To enforce these rules, it demonstrates using Python AST analysis and custom flake8 plugins to ban manual commits and session access outside the DB layer. It also proposes an LLM-based CI/CD check to catch DB models being returned instead of domain models — a case where static typing and AST fall short.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://www.droppedasbaby.com/posts/db-commits>

## Questions this post answers

### How can I detect manual db.commit() calls hidden inside helper functions in a Python codebase?

Write an AST-based test or a flake8 plugin that walks every parsed source file looking for ast.Call nodes where the function is an Attribute named 'commit' or a Name bound to commit, then fail the build if any are found outside the designated DB access layer. This catches manual commits buried two or more levels deep inside helper functions that a transaction context manager wraps, which normal code review often misses.

_Developers hardening transaction boundaries can find AST and linting patterns like this through daily.dev._

### Why can't mypy or type annotations catch a DB model being returned instead of a domain model?

Return type annotations can simply lie about what they return, and functions using cast() launder a DB model's type into a domain model type, so mypy sees only the declared type, not the real object being passed. Because of this, a separate LLM-based CI check is proposed: dump the DB layer's public functions and ask a language model whether any of them return a DB model instead of a domain model, routing 'yes' or 'maybe' answers to human review.

_Anyone debugging leaky type boundaries in an ORM layer can track approaches like this via daily.dev._

## Community discussion

Top comments from developers on daily.dev.

**@trevorsuna** · 0 upvotes

> Transaction ownership is one of those boundaries worth making mechanically enforceable. The AST rule for commits is especially useful because a code-review convention alone is easy to miss when the call is buried several helpers deep.

## Similar posts on daily.dev

- [When the LLM Drifts Away](https://daily.dev/posts/when-the-llm-drifts-away-f5dv1eimx) · Medium · 1 upvotes · 0 comments
- [My OSS Stalled for 3 Months Because of Misguided Vibe Coding—This Is the Full Reboot Story](https://daily.dev/posts/my-oss-stalled-for-3-months-because-of-misguided-vibe-coding-this-is-the-full-reboot-story-rpgzcizq5) · DEV · 32 upvotes · 1 comments
- [The Archaeology of Tech Debt: Auditing a 24-Year-Old Legacy Monolith in 4 Days via Multi-AI Consensus](https://daily.dev/posts/the-archaeology-of-tech-debt-auditing-a-24-year-old-legacy-monolith-in-4-days-via-multi-ai-consensu-jdna570gh) · Coins Bench · 0 upvotes · 0 comments
- [AI didn't delete your database, you did](https://daily.dev/posts/ai-didn-t-delete-your-database-you-did-kkofhdzzw) · Ibrahim Diallo · 88 upvotes · 17 comments

---

Tags: [#python](https://daily.dev/tags/python), [#backend](https://daily.dev/tags/backend), [#domain-driven-design](https://daily.dev/tags/domain-driven-design)

[View this post on daily.dev](https://daily.dev/posts/a-stray-commit-buried-multiple-levels-deep-cost-me-months-auv5pc3lh)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"A stray commit buried multiple levels deep cost me months","url":"https://daily.dev/posts/a-stray-commit-buried-multiple-levels-deep-cost-me-months-auv5pc3lh","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/a-stray-commit-buried-multiple-levels-deep-cost-me-months-auv5pc3lh"},"datePublished":"2026-08-02T00:12:19.530Z","dateModified":"2026-09-13T18:30:01.599Z","description":"A developer shares a painful months-long refactoring experience caused by a stray db.commit() buried deep in helper methods, breaking transaction atomicity....","image":"https://media.daily.dev/image/upload/s--0_ODbtD2--/f_auto/v1722860399/public/Placeholder%2008","thumbnailUrl":"https://media.daily.dev/image/upload/s--0_ODbtD2--/f_auto/v1722860399/public/Placeholder%2008","isAccessibleForFree":true,"articleSection":"Hacker News","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"Hacker News","logo":"https://media.daily.dev/image/upload/t_logo,f_auto/v1/logos/hn","url":"https://daily.dev/sources/hn"},"commentCount":1,"discussionUrl":"https://daily.dev/posts/a-stray-commit-buried-multiple-levels-deep-cost-me-months-auv5pc3lh","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":0},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":1}],"keywords":"python,backend,domain-driven-design","timeRequired":"PT6M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Hacker News","item":"https://daily.dev/sources/hn"},{"@type":"ListItem","position":3,"name":"A stray commit buried multiple levels deep cost me months"}]}
{"@context":"https://schema.org","@type":"WebPage","@id":"https://daily.dev/posts/a-stray-commit-buried-multiple-levels-deep-cost-me-months-auv5pc3lh","comment":[{"@type":"Comment","text":"Transaction ownership is one of those boundaries worth making mechanically enforceable. The AST rule for commits is especially useful because a code-review convention alone is easy to miss when the call is buried several helpers deep.","datePublished":"2026-08-02T04:09:53.678Z","url":"https://daily.dev/posts/AUV5PC3Lh#c-PHQ7oCZiI","author":{"@type":"Person","name":"Trevor Suna","url":"https://daily.dev/trevorsuna","image":"https://media.daily.dev/image/upload/s--dZ7gXxpp--/f_auto/v1784081551/avatars/avatar_EMoP47rpuw8DNjhp6R1b6?_a=BAMAMicg0"}}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/a-stray-commit-buried-multiple-levels-deep-cost-me-months-auv5pc3lh#faq","mainEntity":[{"@type":"Question","name":"How can I detect manual db.commit() calls hidden inside helper functions in a Python codebase?","acceptedAnswer":{"@type":"Answer","text":"Write an AST-based test or a flake8 plugin that walks every parsed source file looking for ast.Call nodes where the function is an Attribute named 'commit' or a Name bound to commit, then fail the build if any are found outside the designated DB access layer. This catches manual commits buried two or more levels deep inside helper functions that a transaction context manager wraps, which normal code review often misses. Developers hardening transaction boundaries can find AST and linting patterns like this through daily.dev."}},{"@type":"Question","name":"Why can't mypy or type annotations catch a DB model being returned instead of a domain model?","acceptedAnswer":{"@type":"Answer","text":"Return type annotations can simply lie about what they return, and functions using cast() launder a DB model's type into a domain model type, so mypy sees only the declared type, not the real object being passed. Because of this, a separate LLM-based CI check is proposed: dump the DB layer's public functions and ask a language model whether any of them return a DB model instead of a domain model, routing 'yes' or 'maybe' answers to human review. Anyone debugging leaky type boundaries in an ORM layer can track approaches like this via daily.dev."}}]}
```

