# 15년 전의 나에게 해주고 싶은 프로그래밍 조언

> Clean Markdown view of GeekNews topic #15848. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=15848](https://news.hada.io/topic?id=15848)
- GeekNews Markdown: [https://news.hada.io/topic/15848.md](https://news.hada.io/topic/15848.md)
- Type: news
- Author: [xguru](https://news.hada.io/@xguru)
- Published: 2024-07-15T11:12:01+09:00
- Updated: 2024-07-15T11:12:01+09:00
- Original source: [mbuffett.com](https://mbuffett.com/posts/programming-advice-younger-self/)
- Points: 69
- Comments: 6

## Summary

- 계속 발에 총을 쏘고 있다면, 총을 고치세요  
- 품질과 속도 사이의 균형 맞추기  
- 톱을 갈고 있는 시간은 거의 항상 가치가 있음  
- 어려움을 쉽게 설명할 수 없다면, 그것은 아마도 우발적 복잡성일 것이고, 이 문제는 해결할 가치가 있음  
- 버그를 한 층 더 깊이 해결하려고 노력하기  
- 버그 조사를 위해 히스토리를 파고드는 것의 가치를 과소평가하지 말 것  
- 나쁜 코드는 피드백을 주지만, 완벽한 코드는 그렇지 않음. 나쁜 코드를 작성하는 쪽으로 오류를 범할 것  
- 디버깅을 쉽게 만들기  
- 팀에서 일할 때는 항상 질문하기  
- 배포 주기가 매우 중요함. 빠르고 자주 배포할 수 있는 방법을 신중히 고민해야 함

## Topic Body

### 계속 발에 총을 쏘고 있다면, 총을 고치세요  
- 팀에서 시스템에 대해 자주 실수하는 부분이 있지만, 그 실수를 줄일 방법에 대해 생각하지 않는 경우가 많음  
- 이런 경우 시스템을 개선하여 실수를 줄이는 것이 중요함  
- 경험:   
  - iOS 개발 시 CoreData를 사용할 때, UI 업데이트는 메인 스레드에서만 가능함  
  - 구독 콜백이 메인 스레드와 백그라운드 스레드에서 모두 발생하여 문제가 종종 생겼음  
  - 기존 팀원들은 이를 인지하고 잘 처리하지만, 신입 팀원의 리뷰에서 종종 언급됨  
  - 가끔 실수가 발생하면 충돌 보고서를 보고 DispatchQueue.main.async를 추가해 왔음   
  - 이를 해결하기 위해 구독 레이어를 업데이트하여 구독자를 메인 스레드에서 호출하도록 변경함. 딱 10분 걸림.  
  - 전체 충돌 클래스와 약간의 정신적 부담을 제거함  
- 누구나 몇 분 동안 생각하면 명백한 문제였을 것임  
- 그러나 이러한 문제를 해결할 자연스러운 시기가 없기 때문에 이상하게 오래 지속됨  
  - 즉 이러한 문제는 팀에 오래 있으면 배경으로 사라지기 쉬움  
- 마음가짐의 변화가 필요함  
  - 이런 문제가 있을 때 자신과 팀의 삶을 더 쉽게 만들 수 있다는 것을 가끔 상기시켜야 함  
  
### 품질과 속도 사이의 균형 맞추기  
- 구현 속도와 정확성에 대한 자신감 사이에는 항상 트레이드오프가 존재함  
  - 현재 상황에서 버그를 출시하는 것이 얼마나 괜찮은지 자문해 보아야 함  
  - 이에 대한 답변이 작업 방식에 영향을 미치지 않는다면, 지나치게 경직된 것임  
- 첫 직장에서 데이터 처리 프로젝트를 할 때는 데이터를 소급하여 재처리할 수 있는 좋은 시스템이 갖추어져 있었음  
  - 버그 출시의 영향은 매우 미미했고, 이런 환경에서는 가드레일에 어느 정도 의존하고, 더 빠르게 움직일 수 있음  
  - 100% 테스트 커버리지나 광범위한 QA 프로세스는 개발 속도를 늦출 뿐임  
- 두 번째 직장에서는 수천만 명이 사용하는 제품으로 고가치 금융 데이터와 개인 식별 정보를 다루어 버그가 치명적이었음  
  - 작은 버그라도 사후 분석이 필요했음  
  - 기능을 매우 느린 속도로 출시했지만, 그 해에 버그를 0개 냈다고 생각  
- 대부분의 경우, 두 번째 회사와 같은 상황에 있지는 않음  
  - 버그가 치명적이지 않은 상황(예: 99%의 웹 앱)에서는 빠르게 출시하고 버그를 빠르게 수정하는 것이 더 나음  
  - 처음부터 완벽한 기능을 출시하는 데 시간을 들이는 것보다 더 발전할 수 있음  
  
### 톱을 갈고 있는 시간은 거의 항상 가치가 있음  
- 도구를 잘 다루는 것이 중요함  
- 코드를 빠르게 작성하고, 주요 단축키를 알고, 운영체제와 셸에 능숙해야 함  
  - 이름 바꾸기, 타입 정의로 이동, 참조 찾기 등을 많이 하게 될 것   
  - 에디터의 주요 단축키를 모두 알고, 자신 있고 빠른 타이핑을 할 수 있어야 함  
  - 브라우저 개발 도구를 효과적으로 사용하는 것도 중요함  
- 도구를 잘 선택하고 능숙하게 사용하는 것은 큰 장점임  
- 새로운 엔지니어에게서 보이는 가장 큰 그린 플래그 중 하나는 도구 선택과 능숙한 사용에 대한 관심임  
  
### 어려움을 쉽게 설명할 수 없다면, 그것은 아마도 우발적 복잡성일 것이고, 이 문제는 해결할 가치가 있음   
- 내가 가장 좋아하는 관리자는 구현이 어렵다고 주장할 때마다 계속해서 압박하는 버릇이 있었음   
  - 종종 그의 대답은 대개 "X를 Y할 때 보내는 것에 불과한 것 아닌가요?" 또는 "몇 달 전에 했던 Z와 같은 것 아닌가요?"와 같은 식이었음  
  - 매우 높은 수준의 반대 의견이었고, 내가 설명하려고 했던 실제 함수와 클래스 수준이 아니었음  
- 관리자가 이렇게 단순화하는 것은 그저 성가신 일이라는 것이 통상적인 견해임  
- 하지만 놀랍게도 높은 비율로, 관리자가 계속 물어볼 때 내가 설명하던 복잡성의 대부분이 우발적 복잡성이라는 것을 깨달았음  
- 그리고 실제로 그것을 먼저 해결함으로써 문제를 관리자가 말하는 것처럼 사소하게 만들 수 있었음  
- 이런 식의 접근은 향후 변경을 더 쉽게 만드는 경향이 있음  
  
### 버그를 한 층 더 깊이 해결하려고 노력하기  
- 버그를 표면적으로 해결하는 대신, 근본적인 원인을 찾아 해결하는 것이 중요함  
- 대시보드에 현재 로그인한 사용자의 상태에서 가져온 `User` 객체를 다루는 React 컴포넌트가 있을때   
  - Sentry에서 렌더링 중에 `user`가 `null`이었다는 버그 리포트가 나옴   
    - `if (!user) return null`을 빠르게 추가하거나   
  - 또는 조금 더 조사해 보면, 로그아웃 함수가 두 개의 별개 상태 업데이트를 수행한다는 것을 알 수 있음   
    - 첫 번째는 사용자를 `null`로 설정하고, 두 번째는 홈페이지로 리디렉션  
  - 두 가지의 순서를 바꾸면, 이제 어떤 컴포넌트도 이 버그를 다시는 겪지 않을 것  
  - 대시보드 내에서는 사용자 객체가 절대 `null`이 아니기 때문  
- 첫 번째 유형의 버그 수정을 계속하면 엉망이 되지만,   
두 번째 유형의 버그 수정을 계속하면, 깨끗한 시스템과 불변성에 대한 깊은 이해를 갖게 될 것  
  
### 버그 조사를 위해 히스토리를 파고드는 것의 가치를 과소평가하지 말 것   
- 나는 `println`과 디버거와 같은 일반적인 도구를 사용하여 이상한 문제를 디버깅하는 데 꽤 능숙했음  
- 그래서 버그의 히스토리를 파악하기 위해 git을 많이 보지 않았는데, 하지만 어떤 버그의 경우에는 이것이 매우 중요함  
- 최근 서버에서 메모리가 지속적으로 누수되는 것 같았고, OOM에 의해 종료되고 다시 시작되는 문제가 있었음  
  - 어떤 가능성 있는 원인도 배제되었고, 로컬에서 재현할 수 없었음  
  - 눈을 가리고 다트를 던지는 것 같은 느낌이었음  
  - 커밋 히스토리를 살펴보니 Play Store 결제 지원을 추가한 후에 발생하기 시작했음  
  - 단지 몇 개의 HTTP 요청일 뿐이라 백만 년이 지나도 찾아보지 않았을 곳임  
  - 첫 번째 액세스 토큰이 만료된 후 액세스 토큰을 가져오는 무한 루프에 빠진 것으로 밝혀짐  
  - 각 요청은 메모리에 1kB 정도만 추가했을 수 있지만, 여러 스레드에서 10ms마다 재시도하면 빠르게 누적됨  
  - 보통 이런 일은 스택 오버플로를 야기했겠지만, Rust에서 비동기 재귀를 사용하고 있었기에 스택 오버플로가 발생하지 않았음  
  - 이는 절대 떠오르지 않았을 것이지만, 문제를 일으킨 것이 분명한 특정 코드를 살펴보게 되면서 갑자기 이론이 떠오름  
- 이런 접근을 언제 해야 할지에 대한 규칙은 없음  
  - 직관에 기반한 것으로, 버그 보고서에 대한 다른 종류의 "어라?"가 이런 종류의 조사를 촉발함  
  - 시간이 지나면서 직관을 발전시킬 수 있지만, 때로는 매우 귀중하다는 것을 아는 것으로 충분함  
- 문제가 적합한 경우 `git bisect`를 시도해 볼 것  
  - 잘못된 것으로 아는 커밋 하나와 좋은 것으로 아는 커밋 하나가 있는 경우  
  
### 나쁜 코드는 피드백을 주지만, 완벽한 코드는 그렇지 않음. 나쁜 코드를 작성하는 쪽으로 오류를 범할 것   
- 끔찍한 코드를 작성하는 것은 정말 쉬움  
- 하지만 모든 모범 사례를 절대적으로 따르는 코드를 작성하는 것도 정말 쉬움   
  - 단위, 통합, fuzz, 돌연변이 테스트를 모두 거쳐야 하는데, 스타트업은 그 전에 돈이 바닥날 것  
- 프로그래밍의 많은 부분은 균형을 찾는 것  
- 빨리 코드를 작성하는 쪽으로 오류를 범하면...  
  - 가끔은 나쁜 기술 부채로 인해 곤란을 겪게 될 것임  
  - "데이터 처리에 대해 훌륭한 테스트를 추가해야 한다"는 것을 배울 것임  
    - 나중에 수정하는 것은 종종 불가능하기 때문  
  - "테이블 설계를 정말 잘 생각해봐야 한다"는 것도 배울 것임  
    - 다운타임 없이 변경하는 것은 매우 어려울 수 있기 때문  
- 완벽한 코드를 작성하는 쪽으로 오류를 범하면...  
  - 아무런 피드백을 받지 못함  
  - 모든 것이 보편적으로 오래 걸림  
  - 어디에 시간을 제대로 쓰고 있는지, 어디에서 시간을 낭비하고 있는지 모름  
  - 피드백 메커니즘은 학습에 필수적이지만, 그런 것을 얻지 못하고 있음  
- "나쁜" 코드의 의미 명확히 하기  
  - "해시맵 생성 구문을 기억할 수 없어서 내부 루프를 두 번 사용했다"는 의미가 아님  
  - 다음과 같은 의미임:  
    - 특정 상태를 표현할 수 없게 하기 위해 데이터 수집을 재작성하는 대신, 몇 가지 핵심 검사점에서 불변성에 대한 어설션을 몇 개 추가함  
    - 서버 모델이 작성할 DTO와 정확히 동일하므로 그냥 직렬화함. 모든 상용구를 작성하는 대신 필요에 따라 나중에 DTO를 작성할 수 있음  
    - 이 컴포넌트들은 사소하고 버그가 있어도 큰 문제가 없으므로 테스트 작성을 건너뜀  
  
### 디버깅을 쉽게 만들기  
- 수년에 걸쳐 소프트웨어를 디버깅하기 쉽게 만드는 많은 작은 트릭들을 습득함  
  - 디버깅을 쉽게 하기 위한 노력을 하지 않으면, 소프트웨어가 점점 더 복잡해짐에 따라 각 이슈를 디버깅하는 데 엄청난 시간을 소비하게 될 것  
  - 변경을 하는 것이 두려워질 것임. 새로운 버그 몇 개를 파악하는 데 일주일이 걸릴 수도 있기 때문  
- 디버깅 시간 중 설정, 재현, 사후 정리에 소요되는 시간을 추적하는 데 주의를 기울일 것  
  - 50% 이상이라면, 이번에는 약간 더 오래 걸리더라도 더 쉽게 만드는 방법을 찾아야 함  
  - 다른 조건이 동일하다면, 버그 수정은 시간이 지남에 따라 더 쉬워져야 함  
  
### 팀에서 일할 때는 항상 질문하기   
- "모든 것을 스스로 알아내려고 노력하는 것"부터 "사소한 질문으로 동료를 귀찮게 하는 것"까지 스펙트럼이 있음  
  - 대부분의 경력 초반자들은 전자 쪽에 너무 치우쳐 있다고 생각함  
- 항상 코드베이스에 더 오래 있었거나, 기술 X를 훨씬 더 잘 알거나, 제품을 더 잘 알거나, 그냥 더 경험 많은 엔지니어가 주변에 있음  
- 어딘가에서 처음 6개월 동안 일하다 보면 한 시간 넘게 무언가를 알아내는 데 시간을 허비하거나, 몇 분 만에 답을 얻을 수 있는 경우가 많음  
- 질문을 할 것. 질문을 하는 것이 누군가에게 성가신 유일한 경우는 몇 분 안에 스스로 답을 찾을 수 있었다는 것이 분명할 때뿐임  
  
### 배포 주기가 매우 중요함. 빠르고 자주 배포할 수 있는 방법을 신중히 고민해야 함  
- 스타트업은 Runway가 제한적이고, 프로젝트에는 마감일이 있음  
- 직장을 그만두고 독립할 때, 저축한 돈은 몇 달 동안만 지속될 것임  
- 이상적으로는, 프로젝트 속도가 시간이 지남에 따라 복리로 증가해서, 상상할 수 있는 것보다 빠르게 기능을 배포하게 됨  
- 빠르게 배포하려면 많은 것들이 필요함  
  - 버그에 취약하지 않은 시스템  
  - 팀 간 빠른 회전 시간(Turnaround)  
  - 새로운 기능의 10%를 잘라내는 의지 (엔지니어링 시간의 50%를 차지할 부분)와 그런 부분을 아는 통찰력  
  - 새로운 화면/기능/엔드포인트에 조합할 수 있는 일관되고 재사용 가능한 패턴  
  - 빠르고 쉬운 배포  
  - 속도를 늦추지 않는 프로세스 (불안정한 테스트, 느린 CI, 까다로운 린터, 느린 PR 리뷰, 종교처럼 여기는 JIRA 등)  
  - 그 외 수백만 가지  
- 느리게 배포하는 것은 프로덕션을 중단하는 것만큼 사후 분석을 해야 함  
  - 우리 업계는 그렇게 운영되지 않지만, 그렇다고 해서 개인적으로 빠른 배포라는 북극성을 따를 수 없는 것은 아님

## Comments



### Comment 27392

- Author: carnoxen
- Created: 2024-07-19T14:17:39+09:00
- Points: 1

"발에 총을 쏜다" = 자승자박  
이란 뜻인가요

### Comment 27551

- Author: yunghn
- Created: 2024-07-25T14:12:15+09:00
- Points: 1
- Parent comment: 27392
- Depth: 1

뭔가 잘못된 코드(고장난 총)로 문제가 생긴다면(발에 쏘기) 총을 고치라는 얘기입니다.

### Comment 27299

- Author: gargoyle92
- Created: 2024-07-16T10:16:53+09:00
- Points: 2

제 머릿속을 그대로 꺼내놓은 것과 같은 충격을 ㄷㄷ..

### Comment 27286

- Author: cbbatte
- Created: 2024-07-16T07:39:20+09:00
- Points: 1

잘 읽었습니다!!

### Comment 27272

- Author: hannah0su
- Created: 2024-07-15T15:24:13+09:00
- Points: 1

잘 읽었습니다.

### Comment 27266

- Author: arfwene
- Created: 2024-07-15T13:35:14+09:00
- Points: 1

개발자는 아니지만 공감이 되는 부분이 많네요
