Case Published

Case: Revising an Earlier Decision During Feature Development

1. Problem

We implement username uniqueness:

main
 └─ A  add users.username
     └─ B  validate username uniqueness
         └─ C  expose username through API
# commit B
validates :username, uniqueness: true

While implementing C, we discover the actual requirement:

Usernames must be unique regardless of letter case.

The change belongs logically to commit B, not after C.

validates :username,
  uniqueness: { case_sensitive: false }

Git cannot mutate B while preserving its descendants:

git commit --fixup B
git rebase -i --autosquash main

Result:

A  → A
B  → B'
C  → C'

The commit graph was rewritten because an earlier implementation decision changed.

2. What broke

The feature is one mutable logical change:

Add usernames
 ├─ persist username
 ├─ enforce case-insensitive uniqueness
 └─ expose username through API

Git represents it only as immutable snapshots:

A → B → C

Git does not represent the semantic relation between the requirement and commit B as a first-class relation.

3. Typical solution

git commit --fixup B
git rebase --autosquash main
git push --force-with-lease

Or developers leave this history:

A  add username
B  validate uniqueness
C  expose through API
D  actually make uniqueness case-insensitive
E  fix tests

4. Coherence solution

The stable unit of work is the changelist, not an individual Git commit:

coherence_slice! {
    changelist "add-usernames" {
        spec "product/usernames" {
            title: "Usernames"
            level: System
            status: Active

            ac "stores-username" {
                title: "Stores the username"
                intent: "A chosen username is persisted for the user"
                risk: Medium
                concerns: [Correctness]

                links {
                    implemented_by file "app/models/user.rb"
                    verified_by test "bundle exec rspec spec/models/user_spec.rb"
                }
            }

            ac "requires-unique-username" {
                title: "Requires a unique username"
                intent: "Duplicate usernames are rejected"
                risk: High
                concerns: [Correctness, Security]

                links {
                    implemented_by file "app/models/user.rb"
                    verified_by test "bundle exec rspec spec/models/user_spec.rb"
                }
            }

            ac "exposes-username-through-api" {
                title: "Exposes the username through the API"
                intent: "API clients can read the user's username"
                risk: Medium
                concerns: [Correctness]

                links {
                    implemented_by file "app/serializers/user_serializer.rb"
                    verified_by test "bundle exec rspec spec/requests/users_spec.rb"
                }
            }
        }
    }
}

While implementing the API criterion, we discover that the uniqueness requirement was incomplete.

The specification changes:

 ac "requires-unique-username" {
     title: "Requires a unique username"
-    intent: "Duplicate usernames are rejected"
+    intent: "Usernames differing only by letter case are rejected"
     risk: High
     concerns: [Correctness, Security]
 }

The changelist and acceptance criterion retain their stable identities:

changelist: add-usernames
ac:         requires-unique-username

The implementation may still produce this temporary Git history:

A  implements stores-username
B  implements requires-unique-username
C  implements exposes-username-through-api
D  corrects requires-unique-username

Git may later rewrite or squash B–D.

Coherence does not pretend that commit B is the stable feature object. It records another version of the same changelist and acceptance criterion, preserving the evolving intent independently of the temporary commit graph.