If you've spent any time on a product team, you've heard this one before:

"Can we just combine first name and last name into one field? Should be quick."

One field instead of two. Five minutes, tops. That's what I thought too, until I actually traced the change through the stack and realized it touched 8 files across two separate codebases, a Next.js frontend and a Strapi CMS backend.

I'm working on Golden Eagles, a community platform for a Poppo Live agency. It has a polls feature where members vote and enter their name. Originally I collected first name and last name separately, but I realized I didn't actually need that granularity. A single "Your name" field would be cleaner. People could enter their first name, a nickname, whatever they want.

Sounds simple. Here's what actually had to change.


1. Copy and translations (src/lib/copy.ts)

The app supports English and Portuguese, so all user-facing strings live in a centralized object with a TypeScript type definition.

I had to remove pollFirstName and pollLastName, add pollName, and update the type. One rename means touching values, types, and both language objects. If I'd just renamed the English string and forgot the Portuguese one, the app wouldn't compile, which is the whole point of having the type, but it still adds surface area.

2. The poll card component (PollCard.tsx)

This was the most involved part. A single component but six separate concerns changed:

  • Props: { firstName, lastName } became { name }
  • State: two useState calls collapsed into one
  • Validation: two required-field checks became one
  • Error messaging: updated the error text
  • API payload: the fetch body went from { firstName, lastName } to { name }
  • JSX: two input elements replaced by one

None of these are hard individually. But if you miss the validation update, users can submit empty names. If you miss the API payload change, the backend rejects the request. Each one is a potential bug that looks fine until someone actually tries to use the form.

3. The polls page (page.tsx)

This is where translations get mapped to component props:

// before
firstName: t.labels.pollFirstName
lastName: t.labels.pollLastName
// after
name: t.labels.pollName

Two lines to one. Small change, but if I'd missed it, TypeScript would have caught the type mismatch. That saved me at least once during this refactor.

4. Next.js API route

The API route that receives the form submission needed:

  • The request body type updated
  • Validation logic changed from checking two fields to one
  • The forwarded payload to Strapi updated
if (!body.name)

Skip this and you get 400 errors from the backend because you're sending fields it doesn't expect anymore, or missing the one it does.

5. Strapi controller

On the backend, the controller that handles incoming poll votes needed the same treatment: destructuring, validation, and the actual database write all referenced the old field names.

// { firstName, lastName } → { name }

6. Poll vote schema (JSON)

Strapi uses JSON schema definitions for its content types. The poll vote schema needed the old firstName and lastName fields removed and a new name field added:

"name": {
  "type": "string",
  "required": true
}

Get this wrong and the database rejects writes, or worse, silently accepts them with null values.

7. Poll schema (content type)

The parent poll content type also references vote structure. Updated the content type definition to remove the old fields and add the new one. This affects the Strapi admin UI, validation rules, and API response shape.

8. Generated types

Strapi generates TypeScript interfaces from its schemas. During development I updated these manually rather than running the full regeneration script, since I was iterating fast and knew exactly which types changed:

name: string

In production you'd re-run the type generator, but either way someone has to verify the types match.


Why this matters for estimation

That "five-minute" change touched:

  • 8 files
  • 2 codebases (frontend + backend)
  • UI, API, validation, database schema, and type definitions

Miss any one piece and you get broken forms, API errors, schema mismatches, or build failures. The change itself isn't hard. None of the individual edits took more than a minute. But the blast radius is wide, and tracing every place the old field names appeared requires understanding the full request lifecycle from input to database.

This is a 3 to 5 point story in sprint planning, and I've watched teams estimate it as a 1. Not because they're bad at estimating, but because the question "how hard is the change?" and "how many places does the change touch?" are completely different questions that feel like the same one.


The part I find interesting (2025 update)

I made all of these changes in a few minutes. Not because I had them memorized, but because the tooling is different now. I described the intent in Cursor, reviewed what it suggested, verified the type chain held up, and pushed. What used to be a grep-fix-run-break-repeat cycle is now closer to "describe intent, review changes."

The complexity didn't go away. There are still 8 files that need to be consistent with each other. But the execution cost dropped dramatically, and that changes how teams can think about these kinds of refactors. The change was always simple. Now the implementation is too.