당신의 일은 단순히 코드를 작성하는 것이 아닙니다. 결정을 내리는 것입니다. 당신은 그 결정으로부터 배웁니다. 시간이 흐를수록 실수는 줄어듭니다. 결국, 당신은 다른 이들이 안개 속을 헤쳐 나갈 수 있도록 안내하게 됩니다. 로직을 작성하는 단계에서 결과에 책임을 지는 단계로 나아가는 그 궤적이야말로, 단순히 구문을 타이핑하는 사람과 시스템을 구축하는 사람을 가르는 차이점입니다.

당신은 매일 선택을 합니다. 버튼 색상을 고르는 것처럼 사소해 보이는 선택도 있고, 제품 전체를 재설계해야 하는 선택도 있습니다. 핵심은 이 두 가지가 서로 연결되어 있음을 조기에 인식하는 것입니다. 부주의하게 내린 작은 결정은 나중에 커다란 제약 사항이 될 수 있는 반면, 초기에 내린 어려운 결정은 나중에 돌이켜봤을 때 천재적인 선택처럼 보이기도 합니다.

초기 선택의 폭발 반경 (Blast Radius)

초기에 실수를 할 때는 그 여파가 좁은 공간에 머뭅니다. 잘못된 커밋 하나가 로컬 빌드를 깨뜨리고, 허술한 함수 하나가 화면 하나를 느리게 만듭니다. 폭발 반경은 좁게 유지됩니다. 영향을 받는 사람은 적고, 복구 비용도 거의 들지 않습니다.

하지만 엔지니어 개인으로서든 회사로서든 성장함에 따라, 당신의 결정은 더 많은 시스템에 걸쳐 영향을 미칩니다. 규모가 커진 상태에서 내리는 똑같은 결정은 몇 주간의 시간을 허비하게 만들 수도 있습니다. 이것이 바로 비용이 커지기 전에 지금부터 계산된 선택을 하는 법을 배워야 하는 이유입니다.

다음 세 가지 흔한 함정을 생각해보십시오.

  • 의존성(dependencies)이 지원하지 않는 플랫폼을 사용하는 것은 수십, 수백 시간의 엔지니어링 시간을 낭비하게 할 수 있습니다. 그 시간은 단순히 타이핑하는 시간이 아닙니다. 기묘한 호환성 문제를 디버깅하고, 전이적 라이브러리(transitive libraries)를 패치하며, 왜 간단한 기능 하나에 한 분기 전체가 소요되었는지 이해관계자들에게 설명하는 시간입니다.

  • 제품 초기 단계에서 세션 기반 인증에서 JWT로 전환하는 것은 나중에 발생할 막대한 재작업을 방지합니다. 사용자가 수백만 명에 달해 다운타임이 실제 비용 손실로 이어질 때보다, 사용자가 수천 명일 때 로그인 로직을 리팩터링하는 것이 훨씬 쉽습니다.

  • 예상 시간의 두 배로 일정을 잡는 것은 그 버퍼를 품질을 보호하는 데 사용할 때만 유효합니다. 소셜 미디어를 보며 시간을 때우기 위해 일정을 늘리는 것은 낭비입니다. 테스트를 작성하고, 예외 케이스(edge cases)를 검토하며, 관측 가능성(observability)을 검증하기 위해 일정을 늘리는 것은 투자입니다.

패턴은 간단합니다. 기술 부채는 복리로 쌓입니다. 원금이 적을 때 갚으십시오.

마감일과 통제의 환상

마감일은 어디에나 있습니다. 출시일, 데모일, 코드 프리즈(code freeze) 등 말이죠. 대기업에서 마감일은 기술적인 목적보다는 심리적인 목적으로 작용하는 경우가 많습니다. 아무도 완전히 이해하지 못하는 복잡성에 대해 통제하고 있다는 느낌을 만들어냅니다.

부작용은 예측 가능합니다. 마감일이 다가올수록 품질은 떨어집니다. 팀은 테스트를 제거하고, 에러 핸들링 코드를 주석 처리하며, 아무도 유지보수하고 싶어 하지 않는 코드를 배포합니다. 마감일은 지켜지고 달력은 깔끔해 보이지만, 제품은 더 나빠집니다.

이는 엔지니어가 완벽한 코드와 우아한 아키텍처를 사랑하기 때문에 발생합니다. 그것은 우리의 본성입니다. 하지만 완벽한 정답이 항상 존재하는 것은 아닙니다. 올바른 선택이란 현재 팀의 상태에 적합한 선택입니다. 3명 규모의 스타트업은 규제를 받는 헬스케어 플랫폼과 같은 격식(ceremony)이 필요하지 않습니다. 5년 전 1,000명 규모의 엔지니어링 조직이 있었던 자리가 아니라, 현재 당신이 처한 위치에 맞춰 구축해야 합니다.

성장이 기존의 규칙을 깨뜨릴 때

이는 리더십이 자주 놓치는 부분입니다. 회사가 성장함에 따라 마감일도 늘어나야 합니다. 프로세스가 확장됩니다. 새로운 인원이 합류하고 온보딩이 필요해집니다. 제품이 늘어나면서 작업량도 배가됩니다. 내부 보안 검토, 외부 감사, 데이터 거버넌스 체크와 같은 컴플라이언스 요구사항도 쌓여갑니다. 다뤄야 할 영역(surface area)은 넓어지는데, 결승선은 제자리에 멈춰 있습니다.

늘어난 업무량에 동일한 마감일을 적용하는 것은 팀을 더 빠르게 만들지 않습니다. 오히려 팀을 부주의하게 만듭니다. 편법을 쓰게 되고, 문서화는 사라지며, 장애 대응은 순전히 사후 약방문식(reactive)이 됩니다. 한때 깨끗한 코드를 배포하던 엔지니어들이 이제는 달력이 유연하게 움직여주지 않는다는 이유로 임시방편(bandages)만을 배포하게 됩니다.

회사가 규모 있는 속도를 원한다면, 업무 트랙을 병렬로 추가하거나 일정을 연장해야 합니다. 세 명의 신규 채용 전에는 타이트하게 느껴졌던 스프린트에, 계속해서 늘어나는 백로그를 압축해서 집어넣을 수는 없습니다.

버퍼를 확보하기

정신 건강을 지켜줄 한 가지 습관은 바로 '무언가 잘못될 것'이라고 가정하는 것입니다. 이것은 비관주의가 아니라 현실주의입니다.

시스템은 실패합니다. 서드파티 API는 지연됩니다. 제품 매니저가 어제 고객과 대화했다는 이유로 요구사항이 바뀝니다. 마찰(friction)을 고려하여 계획을 세울 때, 당신의 마감일은 정직해집니다. 그래야 속도와 품질 사이에서 선택할 수 있는 능력을 얻게 됩니다. 버퍼가 없다면, 매번 선택권은 당신이 아닌 상황에 의해 결정됩니다. 당신은 속도를 선택할 수밖에 없게 되고, 이는 곧 품질을 희생해야 함을 의미합니다.

그 버퍼는 학습이 이루어지는 공간이기도 합니다. 모든 시간을 기능 개발에만 할당한다면, 빌드 파이프라인을 개선하거나, 쿼리 레이어를 리팩터링하거나, API 계약을 문서화할 여유가 아무에게도 없게 됩니다. 팀은 영원히 현재의 속도에 머물게 됩니다.

하나의 오류를 다른 오류로 대체하기

우리는 현재 기묘한 거래를 서두르고 있습니다. 인간의 실수를 비결정론적인 소프트웨어 오류로 대체하고 있는 것입니다. 거대 언어 모델은 어떤 주니어 엔지니어보다도 빠르게 보일러플레이트를 생성하고, 테스트를 제안하며, 문서 초안을 작성할 수 있습니다. 하지만 모델은 확신에 찬 태도로 이를 수행하며, 다음과 같은 방식으로 틀리곤 합니다