0w0

memo

메모

저자는 다양한 개발자를 만나면서, 최고의 개발자들이 가진 공통된 특성에 대해 고민하게 됨

개발 관련2

최고의 PR

­

弊社に5年間在籍していたロシアの天才ハッカーが先日退職しました。

ハッキング世界大会優勝の経歴を持ち、テレビ出演の経験もある彼ですが、正直こんなに長く活躍してくれるとは思っていませんでした。彼のようなタレントが入社した場合、得てして日本の大企業にありがちな官僚主義に辟易してすぐに退職するか、もしくはマスコットキャラとして落ち着くかのどちらかのケースがほとんどなのですが、彼は最後まで現場の第一線で活躍してくれました。

そんな彼が最後に残していった退職メールがなかなか印象的だったので、その拙訳をここに掲載します(転載について本人同意済み。弊社特有の部分は一部省いています。)

ああ、なんという長い旅だったろう。この会社で5年間もセキュリティを担当していたよ(諸々の失敗は許してくれ)

俺は他の退職者のように面白いことは書けないが、私のこの退職メールを読んでくれている人、特に新人エンジニアのために、仕事を改善するための、いくつかの俺の考えを伝えよう。 これを俺の”新人エンジニアサバイバルガイド”と名付ける。中堅エンジニアにも、いくつかのアイデアを汲み取って、新人教育に活用してほしい。 このガイドには、この会社でエンジニアとしてスタートダッシュを決めて、高給取りのエンジニアになるための、俺のちっぽけなアイデアが詰め込んである。

Application Engineer

1.技術的なスキルをマスターしよう。もっともよい教材のリストはこれだ。 https://www.google.com/about/careers/students/guide-to-technical-development.html コースに学生向けと書かれていても気にするな。俺は学生じゃないけどここにあるほとんどのコースを受講した。

2.コードのクオリティに腹をたてるな。同僚の2倍のスピードでリリースをするよう心がけろ。 自分の担当箇所以外でも、明らかに壊れてる箇所を見つけたら、pull requestを送れ。 そうすればお前もイカした奴の仲間入りだ。 品質を確保するためのツールをマスターしろ(IDEs, Jenkins and testing suites, git, ssh)

3.Stack Overflowを使うな。内容をよく理解しないであそこからコピペをするものには、大いなる災いが訪れるであろう。 特にひどいのが暗号化のサンプルだ。99%のサンプルは間違っている!それを使うのではなく、社内のセキュリティチームに相談をしてアドバイスを受けろ。それが賢い人間のやり方ってもんだ。

4.英語を学ぼう。もし海外から来て日本で働いてるなら、日本語を学ぼう。もし両方マスターしてるなら、Javaの勉強でもしたらいいんじゃないかな ;-)

5.Chef, Ansible, Puppetを学ぼう。運用エンジニアの時間を浪費するのはやめよう。

6.常に4つの環境を用意しておこう。3つじゃなくて4つだ。すなわち、開発環境、QA環境(次期バージョンのテスト用)、統合テスト環境(現行バージョンから次期バージョンへのデプロイテスト用)、そして本番環境だ。

Infrastructure Engineer

1.SLAがいくつに設定されているのか、またそれがどのように計算されるかを理解しよう。単一のデータセンターで運用する限り、目標とするSLAには決して到達しないだろう。

2.Failoverにダウンタイムが伴う構成は、high availabilityなシステムとは呼べない。

3.常にTLSを使おう。社内サービスであってもだ。

4.アプリケーションはnginxを経由して公開しよう。

5.システムのハードニングをしよう。そのためのchefのレシピがここにある。 http://dev-sec.io/

6.開発者と一緒に仕事をしよう。彼らが何をしたがってるのか理解するよう努めよう。よりよいソリューションの布教活動をしよう。例えば、キューワーカーモデル、非同期IO、中央レポジトリとフォワーダーによる非同期ロギング、自動化デプロイ、オートスケーリングなどだ。

Database Administrator

1.技術的なスキルをマスターしよう。たとえ、濃縮ウラン認定DBA資格を取ったとしても、学習をやめてはならない。学ぶべきことはまだまだあるのだから。

2.CAP理論を学習し、各データベースソリューションにどのような違いがあるのかを理解しよう

3.それぞれのDBに管理者ごとにユーザを作成しよう。LDAPと連携していればよりベターだ。ログをできるだけ残せ。最低でも監査ログとスロークエリログは忘れるな。そしてそのログをSplunkやKibanaで見れるようにするんだ。

4.証明書ベースの認証をアプリケーションに推奨しよう。そして接続にはTLSを使おう。

5.DBのハードニングをしよう。特にOracleの場合は。 — https://benchmarks.cisecurity.org

6.テスト環境をできるだけ本番環境に近づけよう。もし本番でExadataを使っているならば、ステージング環境、統合テスト環境も同様にそうすべきだ。そこでコストをケチってはいけない。

Security Engineer

この中には新人向けとしては、いささか高度すぎるものも含まれている。

1.不満を述べるのをやめて、修正のためのPull Requestを送ろう。さもなくばマネージャーにでもなったほうがましだろう。でないと、問題を解決することなく、何度も何度も苦しむことになる。

2.セキュリティソリューションを構築せよ。"encryption as a service" や "AppScan as a service" といった人間を介さないサービスを提供できたら素晴らしい。 HashiCorp Vault as a serviceや、TLS証明書管理ツール、 GRCツールなどやるべきことはたくさんある。

3.暗号理論に秀でよ。これは学べば学ぶほど退屈な代物だ。ただ、HMACしてから暗号化と、暗号化してからHMACの違いがわからないようでは、到底エースセキュリティエンジニアとはいえない。

Technical Program Manager

新人の君がこれにあたることはないだろう。この章はむしろエンジニアからマネジメントへのキャリアチェンジを試みている人のためにある。 スポンサーをゲットしろ。スポンサーとは君の案件について、強いモチベーションを持つ役員のことだ。もしそのようなスポンサーがいなければ、その案件は中止し他のもっと重要なものにフォーカスしよう。役員を説得しようとするな。彼らはきっと君の相手に疲れてしまうから。

2.君の案件が最重要案件だと思い込むな。会社には君の案件より重要な無数の最重要案件がある。その時は辛くても、その新しい案件にフォーカスしなおし、時が来るのを待とう。

3.社内の様々な部署の人間とネットワークを構築しよう。

4.経理を学習しよう。

5.プロダクトマネジメントを学習しよう。

6.スピードよりクオリティを優先しよう。急いでもそんなによいことはないし、急がせることは君の責任ではない。よりよいものを提供するために十分な時間を確保しよう。

えーと、これで全部かな。 もしこのアドバイスが横柄に感じられたとしたら謝ろう。 参考にするもよし、無視するもよし、賛成するもよし、しないもよしだ。 これは、すべて俺の経験に基づいた個人的な考えだ。

The Mistakes I Made As a Beginner Programmer

初心者プログラマが犯しがちな間違いと、それらを特定し、避けるための習慣を学ぶ方法。

まず最初に言っておくことがあります。 この記事は、誤りを犯すことを悪いと糾弾するために作成されたものではありません。 むしろ貴方が誤りに自ら気付き、あるいはその兆候を見いだし、それらを避けられるようにするために書かれたものです。

私は過去これらの誤りを犯し、それぞれから学びを得てきました。 今ではこれらを避けるようなコーディングを習慣付けるようにしています。 貴方もそうしましょう。

紹介は順不同です。 設計せずに実装する

高品質なコンテンツは、一般的には容易に作成できるものではありません。 それには慎重な思考と研究が必要です。

高品質なプログラムももちろん例外ではありません。 良いプログラムは、適切なフローに従って書かれるものです。 思考、研究、計画、実装、検証、修正。 残念ながら、これを一言で表す適当な語句がありません。 それぞれのプロセスにどれだけのウェイトを置くか、適切な重み付けを検討する習慣を作る必要があります。

私が初心者だった頃に犯した最大の間違いは、思考や研究をせずいきなりコードを書いたことでした。 これは小さなスダンドアローンアプリには有効な手段かもしれませんが、大規模なアプリには多大な悪影響をもたらします。

考える前に話すことで後悔することがあるかもしれない、と考えるの同様に、考える前にコーディングすることで後悔することがあるかもしれない、と考える必要があります。 コーディングも考えを伝える手段のひとつです。

> 怒ってるときは、口を開く前に10数えよ。激おこであれば100だ。 > > Thomas Jefferson.

私が言い換えるとこうなります。

> コードを書いてるときは、リファクタする前に10数えよ。テストを書いてないなら100だ。 > > Samer Buna

プログラミングとは、主に既存のコードを読むことです。 そして必要な機能と現在の実装の乖離を調査し、どのようにすれば最小限のテストでその差異を埋められるかを設計することです。 実際のコードの記述は、おそらくプロセス全体の10%程度しかありません。

プログラミングとはコードを書くことだ、とは思わないでください。 プログラミングは、成長を必要とするロジカルな創造性です。2) 実装する前に設計しすぎる

コードを書く前に計画を立てるのは良いことです。はい。 しかし、あまりに多くをしすぎると却って悪くなります。 ただの水でも量が過ぎれば毒になるように。

完璧な設計を作り上げようとしてはいけません。 プログラミングの世界にそれは存在しません。 実装を始めるのに必要で十分なレベルの設計を探してください。 実際、設計はよく変わるものです。 適切な設計はコードの構造をクリアにするために必要なものです。 しかし、多すぎる設計は、単に時間の無駄です。

設計は少しずつ行った方がよい、ということです。 全ての機能を一度に設計することは、単純に禁止すべきです。 それはウォーターフォールと呼ばれ、システムを順番にひとつひとつ終わらせていく設計です。 その手法ではいったいどれだけの設計が必要になるでしょう。 ほとんどのソフトウェアプロジェクトでは、ウォーターフォール設計はうまくいきません。 複雑なものになるほどアジャイルでしか対応できなくなります。

プログラム開発は、変化に敏感である必要があります。 ウォーターフォール設計時点では無かった機能を実装することがあるでしょう。 ウォーターフォール設計時点では考慮していなかった理由のために機能を削除することがあるでしょう。 バグは修正し、変化に適応する必要があります。 すなわちアジャイルである必要があります。

ただし、次に実装する予定のいくつかの機能については常に設計してください。 少なすぎる設計も、多すぎる設計も、あなたのコードの品質を損ないます。 そして低品質なコードは、あなた自体の品質に繋がります。3) 品質管理の過小評価

コードを書くときに優先することをひとつだけ挙げるとするならば、それは読みやすさです。 読みにくいコードはガラクタです。 コードはまたリサイクル可能でなければなりません。

コードの品質管理の重要性を過小評価しないでください。 コーディングは実装を伝える手段だと考えてください。 コーダーの主な仕事は、作業中のソリューションの実装を伝えることです。

プログラミングに関する、私のお気に入りのフレーズのひとつを紹介します。

コードを書くときは、メンテナはあなたの住所を知っているサイコパスである、と肝に銘じてコーディングしなさい。 - John Woods

John、素敵なアドバイスをありがとう。

小さなことですら問題です。 たとえば、インデントと大文字小文字が正しくないだけで、あなたにはコーディングの資格がないとみなされます。

tHIS is WAY MORE important than you think

他にすぐわかる不味い点は、長い行です。 80文字を超えるような行はより読みづらくなります。 ifブロックを見やすくするため、複数の条件式を1行にまとめて書きたくなるかもしれません。 そのようなことをしてはいけません。 80文字の制限を超えてはいけません。

このような単純な問題の多くは、Linterや整形ツールで簡単に修正可能です。 JavaScriptであればESLintとPrettierというふたつの優れたツールが存在します。 常にそれらを使うようにしてください。

コード品質については、いくつもの地雷が存在します。 関数の行数が多すぎる

長いコードは、常に個別にテスト可能な小さな単位に分割する必要があります。 個人的には10行以上の関数は長すぎると思っていますが、これはあくまで目安です。 二重否定を使う

使ってはいけません。 二重否定を使うのは非常に悪くなくないです。 短すぎる、汎用的な、データ型を使った命名

変数名には説明的で、曖昧ではない名前を付けます。

> コンピュータサイエンスで難しいことはたった二つだけだ。キャッシュ削除と命名だ。 - Phil Karlton

マジックナンバーのハードコーディング

文字列や数値で固定のプリミティブ値を設定したい場合は、その値を定数に入れ、適切な名前を付けます。

const answerToLifeTheUniverseAndEverything = 42;

問題を回避する

ショートカットや回避策を弄して問題から逃げてはいけません。 現実に直面しましょう。 長いコードが良いと考える

大抵の場合は短いコードの方がよいです。 コードが読みやすくなるのであれば冗長なコードを選びましょう。 コードを短くするために、技巧を凝らしたワンライナーや、三項演算子のネストなどを書いたりしないでください。 とはいえ、必要のないときにまで意図的にコードを長くする必要もありません。 不要なコードを削除することは、プログラムに対して行える最もよい改善点です。

> プログラムの進捗を行数で計るのは、飛行機建造の進捗を重さで量るようなものだ。 - Bill Gates

条件付きロジックの多用

条件付きロジックが必要だと思われているケースの大半では、条件付きロジックが不要です。 代替案も確認し、最も読みやすいと思われる選択肢を選びましょう。 明らかに速度が異ならないかぎり、パフォーマンスを最適化する必要はありません。 またヨーダ記法や条件式中での値代入なども避けましょう。4) 最初の解決策に飛びつく

私がプログラムを始めたころ、提示された問題に対して解決策を見付けたら即座にそれに飛びついていたことを覚えています。 最初の解決策の複雑さの程度について考える前に実装を始め、そして必然的に失敗に結びついていました。

最初の解決策は魅力的かもしれませんが、よくよく考えてみると大抵はより優れた解決策を見付けられます。 問題に対して複数の解決策を考えつくことができないようであれば、それはおそらく問題を完全には理解できていないということです。

プログラマーとしてのあなたの仕事は、問題の解決策を見つけることではありません。 最もシンプルな問題の解決策を見つけることです。 シンプルとは、解決策が正しく適切に機能し、その上で読みやすく、理解しやすく、保守しやすいということです。

> ソフトウェアの設計にはふたつの方法がある。ひとつめは、可能なかぎりシンプルにして明らかに欠陥がないようにすること。もうひとつは、可能なかぎり複雑にして明らかな欠陥がないようにすることだ。 - C.A.R. Hoare

諦めない

私が最も頻繁に犯した間違いが、『最初の解決策が最も簡単な解決策ではないことに気付いた後も、最初の解決策に固執する』ことでした。 諦めない精神が悪い形で出ています。 諦めない精神は、たいていの活動においてはよい心がけですが、プログラミングに適用すべきではありません。 ことプログラミングにおいては、正しい心は早々に死滅し、頻繁に失敗します。

解決策に疑問を覚えたなら、一度それを投げ捨てて問題を再考してみましょう。 その解決策に、それまでどれだけの投資をしていたとしてもです。 gitのようなソース管理ツールは、様々な解決策を使い分けて試してみるのに適しています。

そのコードにどれだけの努力を払ったかは、コードの品質には全く関係ありません。 悪いコードは排除する必要があります。6) ググらない

問題を解決しようとする際に、最初にすべきだったことをしなかったがために多大な時間を浪費したことがたくさんあります。

よほど最先端の技術でも使用していないかぎり、あなたが出会った問題は、大抵の場合誰かが既に同じ問題に遭遇して解決したことのあるものです。 つまり最初にググれということです。

時には、あなたが問題だと思っていたことは別に問題ではないということもGoogleは明らかにしてくれます。 あなたに必要なのは、問題を修正することではなく、むしろそれを受け入れることです。 問題を解決するのに必要なことが全てわかっている必要はありません。 Googleは驚くべき解決策を持ってきてくれるでしょう。

ただし、Googleを使うときは念頭に置いてください。 初心者にありがちな行動のひとつが、出てきたコードを読まずにそのままコピーして使用することです。 たとえ問題を正しく解決するコードだったとしても、理解していないコードは決して使用しないでください。

> クリエイティブな人間として最も危険な考えは、自分がやっていることは自分が知っていることだ、と思い込むことです。 — Bret Victor

カプセル化しない

ポイントは、オブジェクト指向パラダイムを使用するという意味ではないことです。 技術にかかわらずカプセル化の考え方は常に有用です。 カプセル化を行わないシステムは、しばしば保守が困難になります。

アプリケーションには、ある機能を処理する場所は一カ所しかありません。 それは通常、ひとつのオブジェクトの責任です。 そのオブジェクトが公開するのは、外からそのオブジェクトを使うのに必要な最低限の情報だけでなければなりません。 これは機密を保つためではなく、アプリケーションの各部分の依存関係を減らすというコンセプトに基づくものです。 これらの規則に従うことで、どこか離れた場所で何かが動かなくなるのではないかと心配することなく、クラス、オブジェクト、メソッド内部を安全に変更することが可能になります。

クラスは、関連するロジックとステートを集めた概念的集合の単位で作ります。 By class, I mean a blueprint template. This can be an actual Class object or a Function object. モジュール、もしくはパッケージとして識別されることもあります。

ロジッククラスでは、ひとつの独立したタスクにはひとつのメソッドが必要です。 メソッドはひとつのことだけを行い、それを正しく処理しなければなりません。 同じようなクラスは同じメソッドを持っているべきです。

初心者プログラマだった頃、私はどのようにクラスを分けるべきかの概念的集合がよくわからず、何が独立したタスクなのかを切り分けることもできませんでした。 それぞれの関係が特にないような雑多なコードが詰め込まれた"Util"クラスができあがってくれば、それは初心者である兆候です。 一カ所に簡単な変更を加えたところ、別のところに問題が波及し、何カ所も修正を行わなければならなかった場合、それもまた初心者コードの特徴です。

クラスにメソッドを追加する、あるいはメソッドに機能を追加する前に、考える時間を取ってください。 考えを後回しにしたり、後でリファクタリングすればいいやなどと考えたりしないでください。 ここが正しい道への第一歩です。

よい考え方は、コードは高凝集で低結合なものにするということです。 「高凝集で低結合」とは、関連するコードはひとつのクラスにまとめて記述し、異なるクラス間での依存関係を減らす、という意味を表すファンシーな用語です。8) 不要な拡張性を盛り込む

現在の解決策を超えた方法を考えることはしばしば魅力的です。 あなたが書いたあらゆる行で、「もしここで○○だったら」という考えがよぎるかもしれません。 これはエッジケースのテストについて考えるときにはよいことですが、まだ実装されていない対象について対応するのは間違いです。 頭をよぎった「もしここで○○だったら」が、両者のどちらであるかをしっかり分類する必要があります。 今日必要の無いコードは今日書かないでください。 決まっていない事象を織り込まないでください。

> 成長のための成長は癌細胞だ - Edward Abbey

正しいデータ構造を使わない

コードレビューの際、初心者プログラマはアルゴリズムに重点を置きがちです。 適切なアルゴリズムを特定し、必要に応じてそれらを使用するのはよいことですが、それだけでは天才プログラマにはなれないでしょう。

使用している言語に用意されている様々なデータ構造について、その長所と短所を覚えておくと、より良い開発者になれるはずです。

不適切なデータ構造を使うことは、つまり『ここに初心者がいますよ』と触れ回るに等しい行為です。

この記事ではデータ構造の詳細にまでは立ち入りませんが、簡単な例を幾つか挙げておきます。 Using lists (arrays) instead of maps (objects) to manage records

最もよくあるデータ構造選択の誤りは、複数のレコードをmapではなくlistで管理することです。 はい、レコードの管理はmapを使わなければなりません。

以下は各レコードにそのレコードを特定するための一意のキーが存在する場合についての話になります。 スカラー値にlistを使っても問題なく、特に値をpushして使っていた場合にはより良い選択になります。

JavaScriptでは最も一般的なlistはarrayで、最も一般的なmapはobjectです(最近はmapもあります)。

レコードを管理するためにlistを使うのは誤りです。 これが重要である理由は、識別子を使ってレコードを検索する際に、mapはlistより遙かに高速だということです。 実際に目に見える影響が出てくるのはコレクションが巨大になってきたときだけですが、それだけでも固執するには十分な理由です。 Not Using Stacks

繰り返しを必要とするコードを書く場合、単純に再帰を使うのは簡単な選択です。 しかし、再帰コードを最適化するのは非常に難しいです。 特にシングルスレッド環境であればなおさらです。

再帰関数の最適化は、対象によって難易度が著しく異なります。 たとえば2値以上の値を返すコードの最適化は、返り値がひとつだけの関数よりも遙かに難しくなります。

初心者が見落としがちなのが、再帰のかわりに使えるデータ構造があるということです。 それはスタックと呼ばれます。 関数を呼び出すかわりにスタックに積んで、用意が出てきたらpopします。10) コードを悪化させる

このように乱雑な部屋を与えられたと想像してください。

この部屋に新たなアイテムを設置するよう求められました。 部屋は既にじゅうぶん乱雑なので、適当に置いてもかまわないと考えるかもしれません。 その作業は数秒で完了することでしょう。

乱雑なコードでそれをしてはいけません。 決して悪化させてはいけません。 少しでいいから、作業を開始したときよりもコードを綺麗にしましょう。

上の部屋に行うべき正しいことは、新しいアイテムを置くために必要な位置を整頓することです。 たとえば、アイテムが衣服であった場合、まずはクローゼットへの道を片付ける必要があります。 それこそが、あなたが仕事を正しく行うことの一部です。

コードをしばしば悪化させる例を、いくつか挙げておきます。 Duplicating code

コードをコピペして一部の行を変更する行為は重複コードと呼ばれ、コードは純粋に悪化します。 汚れた部屋の例で言うと、高さが調整可能な椅子を導入するのではなく、高さの異なる椅子を導入するようなものです。 できるかぎり抽象的に使うように心がけてください。 Not using configuration files

環境や時間帯によって異なる値を使用したい場合、その値は設定ファイルに書き出しましょう。 コード内の複数箇所でひとつの値を使用したい場合、その値は設定ファイルに書き出しましょう。 コードに新しい値を導入する際は、その値は設定ファイルに書き出すべきかどうかを考えてください。 なお、大抵の場合答えはイエスです。 Using unnecessary conditional statements and temporary variables

全てのif文は少なくとも2回テストする必要がある分岐です。 可読性を下げることなく分岐を避けられるのであれば、そうすべきです。

新たな関数を作るかわりに、関数に分岐ロジックを導入した際によく問題になります。 if文や新たな関数が必要になるたびに、その変更は適切か、もっと異なる次元で対応すべきか、自問してください。

function isOdd(number) {
  if (number % 2 === 1) {
    return true;
  } else {
    return false;
  }
}

isOddはいくつか問題がありますが、最も大きな問題はどこでしょうか。

if文は明らかに不要で、コードは以下と同じです。

function isOdd(number) {
  return number % 2 === 1;
}

意味の無いコメントを書く

できればコメントは書かないようにしたいですが難しいところです。 ほとんどのコメントは、コード内の要素名を適切に置き換えることで排除できます。

// This function sums only odd numbers in an array
const sum = (val) => {
  return val.reduce((a, b) => {
    if (b % 2 === 1) {
      // If the current number is even
      a += b; // Add current number to accumulator
    }
    return a; // The accumulator
  }, 0);
};

このコードは、以下のようにコメント無しで書くことができます。

const sumOddValues = (array) => {
  return array.reduce((accumulator, currentNumber) => {
    if (isOdd(currentNumber)) {
      return accumulator + currentNumber;
    }
    return accumulator;
  }, 0);
};

単に関数名や引数名を適切に付けるだけで、大抵のコメントは不要になります。 コメントを書く前に思い出してください。

しかし、どうしてもコメントを書かなければならない状況に陥ることもよくあります。 それは、このコードは何であるか(WHAT)ではなく、このコードは何故そうなのか(WHY)を説明しなければならないときです。

コードにWHATコメントを書くことが求められている場合でも、明らかに明白なことは書かないでください。

// create a variable and initialize it to 0
let sum = 0;
// Loop over array
array.forEach(
  // For each number in the array
  (number) => {
    // Add the current number to the sum variable
    sum += number;
  },
);

これらはコードにノイズを加えるだけの、全く役に立たないコメントです。 このようなプログラマになってはいけません。 このようなコードを受理してはいけません。 対処が可能ならコメントを削除してください。 部下がこのようなコメントを書いてきたら即座に解雇してください。12) テストを書かない

要点を簡潔に述べましょう。 あなたが自分を、テストを書かずとも思考をそのままコードに落とし込める腕を持っている熟練プログラマである、と考えているのであれば、あなたは初心者です。

テストコードを書いていない場合は、それ以外の方法でプログラムを手動テストすることが多いでしょう。 Webアプリを作っているのであれば、コードを数行書くごとに画面を再描画して確認します。 私もそれは行います。 コードを手動テストするのはおかしなことではありません。 ただし、それと同時にコードを自動テストする方法についても考えておかねばなりません。 手動テストが正常に終わり、コードエディタに戻り、新たなコードを書き、再び手動テストを行う、このように全く同じ動作を行うのであれば同じ動作を自動的に実行するコードを記述しないわけにはいきません。

あなたは人間です。 すなわち、あなたは前回行ったテストの内容を忘れます。 そのような繰り返しはコンピュータにやらせましょう。

可能であれば、コード本体を書き始める前にコードが満たすべき条件を設計・推測するところから始めるとよいでしょう。 テスト駆動開発 ( TDD ) は伊達ではなく、機能やデザインについて考えることにプラスの影響を与えます。 TDDは全ての人に適しているわけではなく、うまく適用できないプロジェクトも存在しますが、とはいえ一部でも適用できるならば導入すべき手法です。13) コードが動いてるなら、コードは正しい

奇数だけを足し合わせるsumOddValues関数を見てみましょう。 何か問題はあるでしょうか。

const sumOddValues = (array) => {
  return array.reduce((accumulator, currentNumber) => {
    if (currentNumber % 2 === 1) {
      return accumulator + currentNumber;
    }
    return accumulator;
  });
};

console.assert(sumOddValues([1, 2, 3, 4, 5]) === 9);

assertは問題なく動作します。よかったよかった。

このコードは未完成です。 このコードはサンプルコードを含めたいくつかのテストを通しますが、それ以上に問題があります。 例を挙げてみましょう。

Problem #1

引数が空の時の処理がありません。 この関数を引数なしで呼び出すとエラーが発生します。

> TypeError: Cannot read property 'reduce' of undefined.

これは2つの点で良くないコードです。 関数の使用者は、その実装の詳細を知りません。 このエラーメッセージは使用者にとって役立ちません。

この関数は機能しませんでしたが、たとえば次のように適切な例外を出したりすることで、その理由が使用者が誤って使ったことだとわかるようになります。

> TypeError: Cannot execute function for empty list.

しかし、おそらくはエラーを投げるかわりに0を返すような設計にした方が適切かもしれません。 何れにせよ、元のままではなく何らかの改修を行う必要はあるでしょう。

Problem #2

不正な入力への対策がありません。 配列ではなく文字列、数値、Objectなどが突っ込まれたときにはどうなるでしょうか。

sumOddValues(42);
TypeError: array.reduce is not a function

array.reduceは間違いなく関数なので、このエラーメッセージはとても残念です。 引き数名にarrayと名前を付けたので、引数は何であれarrayという名前になってしまいます。 エラーメッセージが意味するところは、42.reduceは関数ではないということです。 このエラーメッセージは明らかに混乱を助長するでしょう。 以下のようなメッセージを出した方が有用です。

> TypeError: 42 is not an array, dude.

問題点1と2はエッジケースと呼ばれます。 エッジケースは対応方法がほとんどパターン化されているので、対応方法について考慮を必要とするようなエッジケースはほとんどありません。 以下の例はどうでしょう。

> sumOddValues([1, 2, 3, 4, 5, -13]) // => still 9

-13は奇数であるにもかかわらず、この関数は9を返してきます。 この関数には、この機能が必要ですか? この入力には例外を出す必要がありますか? 負数を受け入れる必要がありますか? 今のように負数を無視するだけでいいですか? そうであれば関数名はsumPositiveOddNumbersのほうが適切でしょう。

上記例ではどのように仕様を決めるかは簡単でした。 重要なポイントは、その仕様を明文化するためのテストケースを書いていなかった場合、将来の保守担当者は負数を無視することが意図的なものかバグなのかわからないことです。

> それは仕様です。 - 詠み人知らず

Problem #3

有効な全てのケースがテストされていません。 この関数には、処理が正しく行われない非常に単純なエッジケースが存在します。

> sumOddValues([2, 1, 3, 4, 5]) // => 11

2は奇数ではないのに結果に含まれてしまっています。 理由は簡単で、reduceに第二引数を渡すとaccumulatorの初期値として扱います。 第二引数を渡さない場合、reduceはコレクションの最初の値をaccumulatorの初期値にします。 これによって、sumOddValuesの結果にはコレクションの1番目の値が常に含まれてしまいます。

コードを書いたときに、これらの値をチェックするテストを書いていれば、問題をすぐに発見することができたでしょう。 テストを最小限しか書かない、エッジケースのテストを書かない、などの兆候は初心者の証です。14) 既存コードを疑わない

あなたが常にソロで働いているスーパースターでもないかぎり、品質のよくないコードに出会わないということはありません。 初心者は出会ったコードの品質を気にせず、正しく動くのだから良いコードだと認識し、自分の知識に取り入れます。

さらに悪いこと、彼らはそれらが良いコードであると思っているため、至る所でその悪いコードを量産するようになります。

一見非常によくないコードのように見えるものもありますが、何らかの特別な理由によってあえてそのように書かれているコードも時にはあります。 そのような場合は、そのコードがどうしてそう書かれているのかを解説するコメントが付いていることが、よいコードの見分け方になります。

初心者であるならば、理解できないうえに解説も書かれていないコードは悪いコードだ、と考えておくとよいでしょう。 それらについて質問し、git blameしましょう。

そのコードの作者が既にいない、あるいは内容を覚えていない場合は、そのコードを調査し、中身を理解しましょう。 コードを完全に把握できたときにのみ、そのコードについての善し悪しを判断しましょう。 理解する前に仮定を当て推量してはいけません。15) ベストプラクティスに拘泥する

"ベストプラクティス"という言葉は有害だと私は考えています。 ベストプラクティスはつまり、これ以上研究する余地はない、疑問を差し挟んではいけない、ということです。

ベストプラクティスなど存在しません。 現時点で、そのプログラミング言語に対しての"グッドプラクティス"が存在するだけです。

かつてベストプラクティスとされていた文法のいくつかは、現在ではバッドプラクティスだと認定されています。

十分に時間をかければ、常によりよい方針を見つけることができるでしょう。 ベストプラクティスにこだわるのをやめ、ベストを尽くすことに集中しましょう。

どこかでそうしろというのを見たから、誰かがそうしているのを見たから、誰かにそう言われたから、だからそうする、ということはしないでください。 これには、私がこの記事で示している全てのアドバイスも含まれています。 全てを疑い、定石に挑戦し、全ての選択肢を調べ、意義のある意思決定を行いましょう。16) 最適化に拘泥する

> 時期尚早な最適化は、プログラミングにおける諸悪の根源だ - Donald Knuth

ドナルド・クヌースがこの発言をして以来、プログラミングは大きく変わりましたが、この発言の重要性は変わっていません。

ひとつ覚えておくべきことは、ボトルネックがどこにあるのかを測定するまでは最適化するべきではないということです。

コードを実行する前に最適化するのは時期尚早であり、そもそも最適化が完全に不要である可能性すらあります。

もちろん、新しいコードを導入する際は常に検討しておくべき最適化項目は存在します。 たとえばNode.jsでは、イベントループを溢れさたり、コールスタックをブロックしたりしないことが非常に重要です。 これらは念頭に置いておくべき、真っ先に最適化するべき例です。 実装するコードはコールスタックをブロックするかについては毎回自問してください。

既存のコードに対して、測定を伴わずに行う最適化は有害であり、避けるべきです。 パフォーマンスの向上と引き替えに、予想外のバグが発生する可能性があります。

発生していないパフォーマンス問題の最適化のために時間を無駄にしてはいけません。17) エンドユーザの視点に立たない

アプリケーションに機能を追加する最も簡単な方法は何でしょう。 自分の立場で考えると、既存のインターフェイスに適合するところを選ぶのが適切でしょう。 その機能がユーザからの情報入力を必要とする場合は、既に存在するフォームに追加します。 その機能がページへのリンクを追加することであれば、既に存在するリンクメニューに追加します。

開発者視点で見てはいけません。 自分がエンドユーザであるという視点で物事を見ましょう。 その機能の使用者は何が必要であるか、ユーザはどのように行動するかを考えましょう。 必要なのは、ユーザがその機能を簡単に見つけて簡単に使える方法であって、簡単に実装する方法ではありません。18) 適切なツールを使わない

誰もが、プログラミングについてのお気に入りツールを持っています。 いくつかのツールは素晴らしいものであり、いくつかのツールはよくないものですが、ほとんどのツールは、ある分野には強力ですが他の分野にはそれほどでもありません。

ハンマーは釘を壁に打ち込むには良い道具ですが、ねじを回すには最悪の道具です。 どれだけハンマーが好きだったとしても、それをねじ回しとして使ってはいけません。 Amazonのユーザレビューで5.0を取っていたとしてもです。

使うツールを人気で選ぶのは初心者の証です。

問題の理由のひとつは、その仕事により適したツールについて知らないことでしょう。 あなたが今使っているツールは、あなたが今知っている中では最良のツールであるかもしれませんが、しかし決して全てのツールの中で最良というわけではありません。 使えるツールには絶えず手を出し、新しいツールについても使い始めることができるようになっている必要があります。

一部のコーダーは新しいツールの使用を拒否します。 彼らは既存のツールに慣れきっており、新しいツールを習得したがらないでしょう。 彼らの気持ちは理解できますが、しかしその態度は単に間違っています。

あなたは原始的なツールで時間を浪費しながら家を建てることも、良いツールにお金と時間を投資した後に短時間でよりよい家を建築することもできます。 ツールは絶え間なく改善されており、それらについて絶え間なく学習し、使っていく必要があります。19) コードの問題がデータの問題に派生する

プログラムの重要な仕事のひとつは、何らかの形のデータの管理です。 プログラムは、新しいレコードを追加したり、古いレコードを削除したり、レコードを変更したりするためのインターフェイスです。

プログラムのバグはほんの小さなものであっても、致命的で予測不可能なダメージをデータに与える可能性があります。 全てのデータがバグのあるプログラムを通っている場合、事態はさらに深刻です。

初心者は、コードとデータの関係性が結びつきにくいかもしれません。 その機能は現在動作しておらず、重要でもないので、気にせずバグの入ったコードを使い続けているかもしれません。 問題は、バグの入ったコードが、データの完全性を誰も気がつかないうちに壊してしまっている可能性があることです。

より悪いのは、データに起こったバグに対応せずにコードのバグだけを修正することです。 そのコードは小さなデータの問題を増幅し、ときに回復不可能なレベルにまでデータが破損してしまいます。

これらの問題からデータを守る方法は? たとえばデータの整合性を検証する複数のレイヤを使用できます。 ひとつのレイヤだけに整合性の担保を頼ってはいけません。 フロントエンド、バックエンド、ネットワーク、およびデータベースのレイヤに検証を入れましょう。 それができないとしても、最低限データベースレベルの制約は使用しなければなりません。

データベースの制約について理解し、テーブルや列を追加するときに使うべき制約は必ず使用しましょう。

NOT NULL

NOT NULL制約は、その列に対してNULL値の設定を禁止します。 アプリケーションがそのフィールドの値を必須としているのであれば、データベースにはNOT NULL制約を入れなければなりません。

UNIQUE

UNIQUE制約は、その列がテーブル全体で重複する値を持つことができない制約です。 たとえばユーザテーブルのユーザ名、メールアドレスなどが指定するのに適切です。

CHECK

CHECK制約は、その式を満たさないかぎりデータを受け入れません。 たとえば、値が0以上100以下でなければならない場合、CHECK制約を使うことでそれを強制できます。

PRIMARY KEY

PRIMARY KEYは、列の値がNULLではなくユニークであることを表します。 おそらくこれは使用しているでしょう。 データベース内の各テーブルには、レコードを識別するためのPRIMARY KEYが必要です。

FOREIGN KEY

FOREIGN KEYは、列の値が、別のテーブルの列、通常はPRIMARY KEYでしょう、と一致しなければならないことを意味します。

新人が犯しがちな、データの整合性に関するもうひとつの問題が、トランザクションという考え方の欠如です。 複数の操作が互いに依存しているデータを変更する場合、それらの操作のひとつが失敗したときに全てを元に戻すために、トランザクションを使う必要があります。20) 車輪の再発明

ここは難しい点です。 プログラミングはいまだに全てがわかっている分野ではなく、いくつかの車輪には再発明する価値があることもあります。 多くの物事が非常に早く変化し、新しい要素がどんどん流入してきます。

例えば、現在の時間帯に応じて異なる速度で回転する車輪が必要になった場合は、既に存在している車輪をカスタマイズするのではなく、おそらく車輪の再設計が必要になるでしょう。 ただし円形をしていないなど、既存設計に則っていない車輪は、それがどうしても必要でないかぎり再発明しないでください。

多くの選択肢の中から、適切なブランドの車輪を選択することはしばしば困難です。 購入する前にいくつかの選択肢を試してみてください。 ソフトウェアの良いところは、その多くが無料であり、車輪の内部を直接見ることができることです。 車輪の品質は、内部のコード設計を見ることで容易に判断できます。 可能であればオープンソースの車輪を使用してください。 オープンソースは簡単にパッケージを修正することができ、簡単に交換することができ、さらに社内でサポートすることもできます。

なお、車輪が必要な場合は、車を1台購入するのではなく、既に所有している車に車輪だけを取り替えてください。 ひとつふたつの関数を使用するためにライブラリ全体を導入してはいけません。 このことに関する適切な例はJavaScriptのlodashライブラリでしょう。 配列をシャッフルするためには、shuffleメソッドだけをインポートすればよいことで、lodashライブラリ全体をインポートする必要はありません。21) コードレビューへの向かい方

初心者に見られる兆候のひとつが、コードレビューを批判と捉えることです。 彼らはそれを好まないし、感謝しないし、剰えそれを恐れさえします。

それは間違っています。 そのように感じたとしたら、すぐに向き合う態度を変える必要があります。 全てのコードレビューは学習の機会と考えてください。 それらを歓迎し、認め、それらから学びましょう。 そして最も重要な点はレビュアーに感謝することです。

あなたは永遠にコードの学習者です。 それを認識しなければなりません。 ほとんどのコードレビューは、あなたが知らなかったことを教えてくれます。 学習リソースであると分類しましょう。

時にはレビュアーが誤っていて、あなたが彼らに何かを教える番になることもあるでしょう。 ただし、あなたのコードだけではどちらが良くないか明らかではないのであれば、コードを修正する必要があるかもしれません。 レビュアーに対して何かを教える機会があるならば、それはプログラマーとして最も有益な活動のひとつです。22) バージョン管理しない

初心者はバージョン管理システムの力を知りません。 ここではGitを対象とします。

バージョン管理は、単に他人のコードに変更を加えてpushするだけのものではありません。 バージョン管理は、つまり歴史です。 コードについて疑念があったときに、そのコードの歴史は疑問を解決してくれる役に立つことでしょう。 従ってコミットメッセージは重要なものになります。 それはあなたの実装が何のために行われたものなのかを表現する手段のひとつです。 コミットはできる限り小規模にすることで、将来のメンテナが、コードが何故そのような状態になっているのかを調査するのに役立ちます。

頻繁に早期に一貫的にコミットし、コミットメッセージは現在形を使用します。 コミットメッセージには要約を、詳しく記載します。 メッセージが数行以上必要になるようであれば、それはコミットが大きすぎるという証なのでrebaseしましょう。

コミットメッセージには不要なものを含めないでください。 たとえば追加変更削除されたファイル名の一覧などは不要です。 それらはコミット自体に入っていて、コマンドで簡単に表示させることが可能なので、単なるノイズでしかありません。 また、ファイルごとに異なるコミットメッセージを付けたいと考える人もいるかもしれませんが、それもおそらくコミットが大きすぎる兆候のひとつでしょう。

バージョン管理は、到達可能性に関するものでもあります。 ある関数の設計や必要性に疑問を感じたときに、その関数が導入されたコミットを見つけて当時の状況を知ることができます。 コミットはまたプログラムにバグが混入されたタイミングを特定するのにも役立ちます。 bisectコマンドを使って、バグが入ってきたコミットをバイナリ検索で特定することができます。

バージョン管理は、変更を公式に取り込むという用途以外でも大活躍します。 ステージング環境の構築、パッチの選択、修正のリセット、棚上げ、コミットの修正、差分の確認、取り消しなどなど、コードフリーに機能豊富なツールが追加されます。 これらを理解し、学習し、使用しましょう。

知っているGitの機能が少ないほど初心者に近いと言えます。23) グローバル使いすぎ

ここでは関数型プログラミングとそれ以外のパラダイムの差異を話す、わけではありません。 それは別の記事でやります。

ここで話すのは、グローバルは全ての元凶であり、可能な限り避けるべきものである、という事実にすぎません。 万一それが不可能である場合でも、グローバルの使用は必要最小限に抑えなければなりません。

私が初心者だったころに認識していなかったこととして、定義した全ての変数は共有状態にあるということがあります。 要するにその変数は、その変数と同じスコープにある全ての要素から操作可能であるということです。 スコープがグローバルに近づくほど、この共有状態の治安は悪化します。 可能な限り小さなスコープを保ち、外側に流出しないようにしてください。

複数のリソースが同時にひとつの変数を操作しようとしたときに、グローバルの大きな問題が発生します。 レースコンディションです。

初心者は競合状態、特にデータのロックについての問題への回避策として、タイマーを使用したがる傾向があります。 それは危険信号です。 してはいけません。 コードレビューで指摘し、拒絶してください。24) エラーを嫌う

エラーはよいことです。 コードが前進していることを意味します。 つまり、改善のフォローアップが簡単に行えます。

プロの開発者はエラーを愛しますが、初心者は嫌います。

エラーが気に障ると考えているのであれば、エラーへの態度を改める必要があります。 エラーはヒントであり、対処し、活用するものであると考えましょう。

いくつかのエラーは例外に改修する必要があります。 例外は、ユーザが定義するエラーの一種です。 また、他のいくつかのエラーはそのままにしておく必要があります。 それらはアプリをクラッシュさせて強制終了させます。25) 休憩を取らない

あなたはおそらく人間であり、脳には休憩が必要です。 あなたの身体には休息が必要です。

あなたはしばしばゾーンに入り、寝食を忘れて集中することもあるでしょう。 私はそれを初心者の兆候であると考えています。 これは何某かで代替できるようなものではありません。

ワークフローに休息を統合しましょう。 短い休憩をたくさん取り、机から少し離れて周囲を歩き、次に何をするか考えてみましょう。 心機一転してコードに戻ってください。

このポストはとても長いものでした。あなたは休憩が必要です。 読んでくれてありがとう。


The Mistakes I Made As a Beginner Programmer

초보 프로그래머 범하는 실수들을 특정해 그것을 피하는 습관을 배우는 방법.

우선 처음으로 말해둘 것이 있습니다.

이 글은, 실수를 일으키는 것이 나쁘다는 전제로 쓴 글이 아닙니다.

오히려 실수를 스스로 깨닫아 그것을 징후를 간파해, 피하기 위한 글입니다.

나는 과거에 이런 실수를 범해 여러가지를 배웠습니다.

지금은 이것을 피하기 위한 코딩을 습관으로 가지고 있습니다.

당신들도 그렇게 할 수 있습니다.

순서는 무작위입니다. 설계하지 않고 구현한다

품질 높은 컨텐츠는 일반적으로 용의주도하게 작성하지 않으면 안됩니다.

그러기 위해서는 신중한 사고와 연구가 필요합니다.

품질 높은 프로그램도 물론 예외가 아닙니다.

좋은 프로그램은 적절한 흐름에 의해 적어있어야합니다.

생각, 연구, 게획, 구현, 검증, 수정.

아쉽게도 이를 한 마다로 표현할 적당한 말은 없습니다.

각각의 프로세스에 어느 정도의 비중을 둘까, 적절한 가중치를 검토하는 습관을 만드는 것이 중요합니다.

내가 초보일 때 범한 큰 실수는 생각, 연구하지 않고 갑자기 코드를 적는 것이었습니다.

이것은 작은 독립 앱에서는 유효한 수단일지 모르나, 대규모 앱에는 엄청난 악영향을 끼칩니다.

생각하기 전에 말해 후회한 적이 있을지도 모릅니다.

이런 생각처럼, 생각하기 전에 코딩하는 것을 후회할지 모릅니다.

생각할 필요가 있습니다.

코딩도 생각을 전달하는 수단의 하나입니다.

화가 나면, 입을 열기 전에 10을 세라, 개빡쳤다면 100을 세라. Thomas Jefferson.

제가 바꿔 말하면 이렇게 됩니다.

코드를 쓸 때는, 리팩토링 전에 10을 세라, 테스트를 적지 않았다면 100을 세라. Samer Buna

프로그래밍이란 주로, 기존의 코드를 읽는 것입니다.

그리고 필요한 기능과 현재 구현의 괴리를 조사해, 어떻게 해야 최소한의 테스트로 그 차이를 메울 수 있는지 설계하는 것입니다.

실제 코드 작성은 이 프로세스 전체의 10%정도 밖에 되지 않습니다.

프로그래밍은 코드를 적는 것이다 생각하지 맙시다.

프로그래밍은 성장을 필요하는 논리적 창조성입니다. 구현하기 전에 설계를 너무한다

코드를 적기전에 계획을 세우는 것은 좋습니다. 맞습니다.

그러나 너무 과하면 오히려 나빠집니다.

물을 너무 많이 마시면 독이 되는 것처럼.

완전한 설계를 하려는 것은 안 됩니다.

프로그래밍 세계에 그것은 존재하지 않습니다.

구현을 하기위해 필요한 적당한 수준의 설계를 찾아봅시다.

실제 설계는 자주 바뀌는 것 입니다.

적절한 설계는 코드 구조를 클린하게 하기 위해서 필요합니다.

하지만 과한 설계는 그저 시간을 허비하는 것입니다.

설계는 조금씩 하는 것이 좋습니다.

모든 기능을 한 번에 설계하는 것은 단호하게 금지해야합니다.

그것은 워터폴이라 부르며, 시스템 순서에 하나하나 완성시켜 설계하는 것입니다.

이 방법에는 도대체 어느 정도의 설계가 필요해질까요.

대부분의 소프트웨어 프로젝트에는 워터폴 설계는 제대로 돌아가지 않습니다.

복잡하면 할수록 애자일외에는 방법이 없습니다.

프로그램 개발은 변화에 민감할 필요가 있습니다.

워터폴 설계시점에는 없던 기능을 구현한 적이 있을 것입니다.

워터폴 설계시점에 고려하지 않았다는 이유로 기능을 삭제한 적도 있을 것입니다.

버그는 수정하고, 변화에 적응할 필요가 있습니다.

즉 애자일이어야 할 필요가 있습니다.

단, 다음 구현할 기능에 대해서 미리 설계해야합니다.

너무 적은 설계도 너무 많은 설계도 당신의 코드 품질을 깍아먹습니다.

그리고 품질 낮은 코드는, 당신 자체의 품질과 연결됩니다. 품질관리의 과소평가

코드를 적을 때 우선해야할 한가지는 그것은 "읽기 편함"입니다.

읽기 불편한 코드는 잡동사니입니다.

코드는 재활용할 수 있어야합니다.

코드의 품질관리의 중요성을 과소평가하지 맙시다.

코딩은 구현을 전달하는 수단이라 생각합시다.

코더의 주 임무는, 작업한 솔루션의 구현을 전달하는 것입니다.

프로그래밍에 관해 마음에 드는 문구를 소개합니다.

코드를 적을 때는, 유지관리자가 당신의 주소를 아는 사이코패스라는 것을 잊지말고 코딩해라. Jonh Woods

John의 조언의 훌륭합니다.

작은 것조차 문제입니다.

만약에 인텐트와 대문자, 소문자가 바르게 사용되지 않으면, 당신은 코딩할 자격이 없다 여겨집니다.

tHIS is

WAY MORE important

than

         you think

또 금방 알 수 있는 찝찝한 점은 긴 행입니다.

80자 넘은 행만큼 읽기 괴로운 것은 없습니다.

if 블록을 보기 좋게 하기 위해서 복수의 조건식을 1행에 넣어버리고 싶을지도 모릅니다.

그러나 그딴 짓을 하면 안 됩니다.

80자 제한을 넘겨서는 안 됩니다.

이렇게 단순한 문제의 대부분은 Linter나 제형 도구로 간단히 수정가능합니다.

JavaScript라면 ESLint와 Prettier 이 두가지 우수한 도구가 존재합니다.

반드시 이들을 사용하도록 합시다.

코드 품질에는 몇 가지 지뢰가 존재합니다.

함수의 행수가 너무 많다.

긴 코드는 항상 단위 테스트 가능한 작은 단위로 분할할 필요가 있습니다.

개인적으로 10행 이상 함수는 너무 길다 생각한다. (필수가 아닌 가볍게 지향점이라 여기면 됩니다.)

이중부정을 사용한다.

사용하면 안 됩니다.

이중부정을 사용하는 것은 무지하게 나쁜 것이라 생각하지 않을 수가 없습니다.

(이중부정을 사용하는 것은 무지하게 나쁜 것입니다.)

너무 짧거나, 범용적인 데이터 타입을 사용한 명명

변수명에는 설명이 들어가야하며, 애매하지 않은 이름을 붙여야합니다.

CS에 어려운 점은 캐시삭제랑 이름 짓기, 단 2개입니다. Phil Karlton

매직넘버 하드코딩

문자열이나 수치에 고정된 값을 설정하고 싶은 경우에는 값을 변수에 넣어, 적절한 이름을 붙입시다.

const answerToLifeTheUniverseAndEverything = 42;

문제를 회피합니다.

쇼트 커트나 회피책을 그릇한 문제에서 피하면 안 됩니다.

현실을 직면합시다.

긴 코드가 좋다 생각합시다.

대부분의 경우 짧은 코드가 좋습니다.

코드가 읽기 좋게하려면 긴 코드를 고릅시다.

코드를 짧게하기 위해서는 기교를 부려서 one-liners나 삼항연산자를 쓰지 맙시다.

하지만 필요하지 않는데 의도적으로 코드를 길게 쓸 필요는 없습니다.

하지만 필요하지 않는 부분까지 의도적으로 코드를 길게할 필요는 없습니다.

불필요한 코드를 삭제하는 것이 프로그램에 최적의 가장 좋은 개선방법입니다.

프로그램 진척을 행수로 계산하는 것은 비행기 구조의 진취를 무게로 정하는 것과 같습니다. Bill Gates

조건 로직의 과용

조건 로직이 필요한 경우의 태반은 조건 로직이 불필요합니다.

대체방법을 확인해, 가장 읽기편한 선택지를 고릅시다.

명백하게 속도가 차이나지 않는 경우, 퍼포먼스을 최적화할 필요가 없습니다.

또 요다기법이나 조건식 중에 대입도 피합시다. 최초의 해결책에 몰두한다.

프로그램을 시작할 때, 게시된 문제에 대해 해결책을 발견하면 즉석에서 그것에 몰두한 적이 있습니다.

처음 해결책은 복잡한 정도에 대해 생각하기 전에 구현을 시작해, 그것은 필연적으로 실패로 귀결됩니다.

처음 떠올린 해결책은 매력적일지도 모르겠지만, 잘 생각해보면 대체로 더 좋은 방법을 발견할 수 있습니다.

문제에 대해 복수의 해결책을 생각한다는 것은 문제를 완전히 이해하고 있다는 소리입니다.

프로그래머로 당신의 일은 문제의 해결책을 찾는 것이 아닙니다.

더욱 간단하게 문제를 해결책을 찾는 것입니다.

간단하게라는 뜻은, 해결책이 똑바로 적절하게 기능하면서, 읽기도, 이해하기도, 보수하기도 쉬운 것이라는 뜻입니다.

소프트웨어 설계에는 두가지 방법이 있다.

하나는 가능한 간단하면서, 명백하게 결함이 없는 것

다른 하나는 가능한 복잡하면서, 명백하게 결함이 없는 것이다. C.A.R Hoare 포기하지 않는다.

가장 높은 빈도로 범한 실수는 [처음 해결책이 가장 간단한 해결책이 아니라는 것을 눈치채고도 그 방법을 고집한다] 입니다.

포기하지 않은 정신의 나쁜 버전입니다.

포기하지 않는 정신은 대체로 활동에 좋은 방법이지만, 프로그래밍에 적용하면 안 됩니다.

프로그래밍에서 올바른 마음은 빨리 사라지며, 빈번히 실패합니다.

해결책의 의문을 갖는다면, 한 번 그것을 던져버리고 생각을 다시해봅시다.

그 해결책에 얼마나 많은 투자를 했어도 말입니다.

git과 같은 소스관리 도구는 다양한 해결책을 나눠 테스트하는 것에 최적화 되어 있습니다.

그 코드에 얼마나 많은 노력을 했어도, 코드 품질에는 전혀 관계없는 소리입니다.

나쁜 코드를 배제할 필요가 있습니다. 검색하지 않는다.

문제를 해결하려할 때, 처음에 해야할 것을 하지 않아 많은 시간을 소비한 적이 많습니다.

정말 최첨단 기술에서 사용하는 것이 아니라면, 당신이 만난 문제는, 대체로 누군가가 이미 같은 문제를 만나 해결한 것이기도합니다.

즉, 처음에는 검색하자는 소리입니다.

경우에 따라 당신이 문제라 생각하는 것이 문제가 아니라 대답해주기도 합니다.

당신에게 필요한 것은, 문제를 수정하는 것이 아니라, 오히려 받아 들이는 것입니다.

문제를 해결하기에는 필요한 것을 모두 알 필요가 없습니다.

인터넷이 놀라울 정도로 해결책을 가지고 있습니다.

물론, 인터넷을 사용할 때 염두해주세요.

초보에게 있을 법한 행동 중에 하나가, 읽지않고 그대로 사용하는 것입니다.

만약 문제를 정확히 해결하는 코드라해더라도, 이해하지 않은 코드를 결코 사용하지 말아주세요.

크리에이티브한 사람하는 가장 위험한 생각은, 자신이 무엇을 하는지 잘 알고 있다 생각하는 것입니다.

-Bret Victor 캡슐화하지 않는다.

요점은 OOP를 이용하라는 소리가 아닙니다.

기술에 상관없이 캡슐화를 생각하는 것이 좋습니다.

캡슐화를 하지 않은 시스템은, 점점 보수가 곤란해집니다.

앱에서 어떤 기능을 처리하는 경우는 하나에 불과합니다.

그리고 통상적으로 하나의 오브젝트의 책임입니다.

오브젝트가 공개하는 것은, 밖에서 오브젝트를 사용할 때 필요한 최소한 정보입니다.

이것은 기밀을 갖기 위함이 아니라, 앱의 각 부분의 의존관계를 적게하기 위한 컨셉을 만들기 위함입니다.

이 규칙에 따라, 어딘가 떨어진 장소에 무엇인가 안 움직이지일까 걱정하지말며, 클래스, 오브젝트, 메서드 내부를 안전하게 변환하는 것이 가능해집니다.

클래스는 관련할 로직과 스테이트를 모은 개념적 집합의 단위로 만들어집니다.

By class, I mean a blueprint template.

This can be an actual Class object or a Function object.

모듈, 혹은 패키지로 식별할 때도 있습니다.

로직클래스에서는 하나의 독립된 태스크에 하나의 메서드가 필요합니다.

메서드는 하나의 행동만 하며, 그것이 정확히 처리되어야 합니다.

같은 클래스는 같은 메서드 가져야합니다.

초보프로그래머일 때, 저는 어떻게 클래스를 나눠야 하는가 개념적 집합을 잘 몰라서, 무엇인 독립한 태스크인가 잘라 나누는 것이 불가능했습니다.

각 관계가 특별히 없도록, 잡다한 코드를 우겨넣은 "Util" 클래스가 있다면, 이는 초보라는 증거입니다.

한 곳에 간단히 변경을 가하는 것으로, 다른 곳에서 문제가 전파되어, 몇 곳이나 수정하지 않으면 안되는 경우, 그것도 초보자 코드의 특징입니다.

클래스 메서드를 추가한다, 혹은 메서드에 기능을 추가하기 전에, 생각할 시간을 가지기 바랍니다.

생각을 나중으로 미루거나, 나중에 리팩토링하면 되지, 생각하는 것을 멈추기 바랍니다.

이것이 올바른 길로 가는 첫 걸음입니다.

좋은 생각은 코드를 응집도를 높히고, 결합도를 낮추는 것입니다.

[고응집, 저결합]이란, 관련하는 코드는 하나의 클래스로 정리해서 적으며, 각 클래스 사이의 의존관계를 줄인다는 의미의 표현하는 귀여운 용어입니다. 불필요한 확장성을 담는다.

현재 해결책을 뛰어 넘는 방법을 생각하는 것은 꽤 매력적입니다.

당신이 쓴 이런 저런행에 [만약 여기에 ㅇㅇ가 있으면] 이런 생각을 떠올릴 수도 있습니다.

이는 엣지 케이스 테스트에 대해 생각할 때는 좋은 방법이지만, 아직 구현되지 않은 대상에 대해 대응하는 것은 실수입니다.

머리가 떠올린 [만약 여기에 ㅇㅇ가 있으면]은 둘 중 무엇인지 반드시 분류할 필요가 있습니다.

오늘 필요하지 않은 코드는 오늘 쓸 필요가 없습니다.

결정되어 있지 않은 일을 집어넣지 마십시오.

성장하기 위한 성장은 암세포이다.

Edward Abbey

올바른 자료구조를 사용하지 않는다.

코드리뷰를 할 때, 초보 프로그래머가 알고리즘에만 중점을 두고는 합니다.

적절한 알고리즘을 특정해서, 필요에 응해 그것을 사용하려하는 것은 좋습니다만, 그것으로 천재 프로그래머가 될 수 없습니다.

사용하고 있는 언어에 준비된 다양한 자료구조에 대해, 그 장점과 단점을 기억하시면 더 좋은 개발자가 될 것 입니다.

부적절한 자료구조를 사용하는 것은, 즉 [여기에 초보가 있습니다.] 말하는 것과 같은 행동입니다.

이 글에서는 자료구조의 상세한 것까지 다루지 않지만, 간단한 예시를 몇 올리겠습니다.

Using lists(arrays) instead of maps (objects) to manage records

가장 좋은 자료구조 선택에서 실수는 복수의 레코드를 map이 아니라 list로 관리하는 것입니다.

맞습니다, 레코드 관리는 map를 사용하면 안됩니다.

이하의 각 레코드에 그 레코드를 특정하기 위한 하나의 키가 존재하는 경우에 대해서만 다룹니다.

스칼라값에 list를 사용하면 문제없이, 특히 값을 push해서 사용하는 경우 더 많은 선택이 됩니다.

JavaScript에는 우선 일반적으로 list는 array로, 가장 일반적인 map은 object입니다.(최근에는 Map도 있습니다.)

레코드를 관리하기 위해 list를 사용하는 것은 잘 못 된 것입니다.

이것이 중요한 이유는 식별자를 사용해서 레코드를 검색하는 경우, map은 list보다 현저히 빠르기 떄문입니다.

실제로 눈에 보이는 영향이 보이는 것은 콜렉션이 거대했을 경우입니다만, 그것만으로도 고집할 충분한 이유입니다.

Not Using Stacks

반복을 필요할 때, 단순히 재귀를 선택하면 됩니다.

하지만, 재귀코드를 최적화하는 것은 꽤 어렵습니다.

특히 싱글 스레드 환경이라면 더욱 그러합니다.

재귀함수의 최적화는 대상에 의해 난이도가 현저히 달라집니다.

만약 2개 이상 값을 반환하는 코드의 최적화라면, 반환이 하나인 함수에 보다 무척 어려워집니다.

초보자가 그냥 넘기는 경우도 있습니다만, 재귀 대신 사용할 수 있는 자료구조가 있다는 것입니다.

바로 스택입니다.

함수를 호출하는 대신에 스택을 쌓아, 완료되면 pop합시다. 코드를 악화시킨다.

지저분한 방을 상상해보세요.

이 방에 아이템을 설치해야한다는 요구를 들었습니다.

방은 이미 충분히 난잡하기에, 적당히 둬도 괜찮지 않을까 생각할 수 있습니다.

이 작업은 몇 초면 되지요.

난잡한 코드에는 그러면 안됩니다.

절대 악화시키면 안됩니다.

조금이라도 좋으니, 작업을 개시하기 전에 코드를 정리합시다.

지저분한 방에 해야할 일은, 새로운 것을 두기 위해 필요한 위치를 정돈하는 겁니다.

예를 들어 둬야할 것이 옷이라면, 우선은 옷장을 정리해야겠지요.

그것만으로, 당신는 올바른 일을 하기 위한 첫 발을 뗀 것입니다.

코드를 점점 악화시키는 방법을 몇 가지 들어보겠습니다.

Duplicating code

코드를 복붙해 일부 행을 변환하는 행위는 중복코드를 부르며, 코드의 순수성를 악화시킵니다.

더러워진 방을 예로 들어면 높낮이가 조절되는 의자를 넣는 것이 아니라, 높낮이가 다른 의자를 넣는 것입니다.

되도록 추상적으로 사용하는 것을 마음에 두십시오.

Not using configuration files

환경이나 시간대에 따라 다른 값을 사용하고 싶은 경우, 이 값은 설정 파일에 적어둡시다.

코드 안에 여러 각 자리에 하나의 값을 사용한 경우, 그 값은 설정 파일에 적어둡시다.

코드에 새로운 값을 도입하는 경우는, 그 값은 설정파일에 적어둡시다.

대부분의 경우 옳은 길입니다.

Using unnecessary conditional statements and temporary variables

모든 if문은 적어도 2회 테스트할 필요가 있는 분기입니다.

가독성을 낮추지 않으며, 분기를 피할 수 있다면, 그렇게 해야합니다.

새로운 함수를 만드는 대신, 함수에 분기로직을 도입한 경우 종종 문제가 됩니다.

if문이나 새로운 함수가 필요가 생길 때마다, 그 변경은 적절한가 다른 차원에 대응할까 자문해 보십시오.

function isOdd(number) {
  if (number % 2 === 1) {
    return true;
  } else {
    return false;
  }
}

isOdd는 몇 가지 문제가 있습니다만, 가장 큰 문제는 어디일까요?

if문 명백히 필요없으므로, 이렇게 쓰는 것과 같습니다.

function isOdd(number) {
  return number % 2 === 1;
}

의미없이 주석을 쓴다.

되도록 주석은 쓰지 않는 것이 좋은데 어려운 일입니다.

대부분의 주석은 코드 내의 요소명을 적절히 바꿔 쓰는 것으로 배제할 수 있습니다.

// This function sums only odd numbers in an array

const sum = (val) => {
  return val.reduce((a, b) => {
    if (b % 2 === 1) {
      // If the current number is even

      a += b; // Add current number to accumulator
    }

    return a; // The accumulator
  }, 0);
};

이 코드는 이렇게 쓰면 주석을 쓸 필요가 없습니다.

const sumOddValues = (array) => {
  return array.reduce((accumulator, currentNumber) => {
    if (isOdd(currentNumber)) {
      return accumulator + currentNumber;
    }

    return accumulator;
  }, 0);
};

단순히 함수명이나 인수명을 적절히 붙이는 것으로, 대부분의 주석은 필요하지 않습니다.

주석을 쓰기 전에 생각해보세요.

하지만, 어떻게든 주석을 쓰지 않으면 안되는 경우가 생기기도 합니다.

그럴 때는 이 코드는 무엇인지(WHAT)이 아니라, 이 코드는 왜 그렇게 되는가 (WHY)를 설명해야합니다.

코드에 WHAT 주석을 쓰는 것을 요구받는 경우에도, 너무 명백한 것을 적지 말아주세요.

// create a variable and initialize it to 0

let sum = 0;

// Loop over array

array.forEach(
  // For each number in the array

  (number) => {
    // Add the current number to the sum variable

    sum += number;
  },
);

이는 코드에 잡음을 넣는 것으로, 전혀 도움이 되지 않습니다.

이런 프로그래머가 되면 안됩니다.

또 저렇게 처리하면 안됩니다.

대처가 가능한 주석을 삭제하세요.

부하가 이렇게 쓰면 바로 한 마디 해줍시다. 테스트를 적지 않는다.

요점을 간결히 말하겠습니다.

당신이 자신은 테스트를 쓰지않아도 생각을 그대로 코드로 옮겨 적을 수 있는 숙련된 프로그래머라 생각한다면 당신은 초보입니다.

테스트 코드를 쓰지 않은 경우, 코드 외의 방법으로 프로그램을 수동테스트것이 많을 때일 것입니다.

Web 앱을 만든다면, 코드 조금 쓸 때마다 화면을 새로고침해서 확인합니다.

저도 그러합니다.

코드를 수동테스트 하는 것은 이상한 것이 아닙니다.

그러나, 그것과 동시에 코드를 자동테스트하는 방법에 대해서도 생각하지 않으면 안됩니다.

수동테스트가 정상적으로 마친 후에, 코드에디터로 돌아가, 새로운 코드를 적고, 다시 수동테스트... 이런 식으로 몇 번이고 같은 짓을 한다면, 이걸 자동적으로 수행하는 코드를 쓰는 것이 좋을 것입니다.

당신은 사람입니다.

즉, 당신은 몇 번 테스트한 내용을 잊어버립니다.

이러한 반복은 컴퓨터에게 시키십다.

가능하다면, 코드 본체를 쓰기 전에, 코드가 가져야 할 조건을 설계, 추측하는 것부터 시작해봅시다.

테스트 주도 개발(TDD)을 말만이 아니라, 기능이나 디자인에 대해서도 긍정적인 영향을 줍니다.

TDD 모든 사람에게 적절한 것은 아니기에, 잘 적용되지 않은 프로젝트도 있습니다.

그렇지만 일부라도 적용할 수 있다면 바로 해야할 기법입니다. 코드가 움직인다면, 코드는 옳다.

홀수만 더하는 sumOddValues 함수를 봅시다.

무엇이 문제일까요?

const sumOddValues = (array) => {
  return array.reduce((accumulator, currentNumber) => {
    if (currentNumber % 2 === 1) {
      return accumulator + currentNumber;
    }

    return accumulator;
  });
};

console.assert(sumOddValues([1, 2, 3, 4, 5]) === 9);

assert은 문제없이 동작합니다. 메테타시 메테타시.

이 코드는 미완성입니다.

이 코드는 샘플 코드를 포함해 몇 몇 테스트를 통과했지만 그 이상 문제가 있습니다.

예를 들어보겠습니다.

Problem #1

인수가 비어있을 때 처리가 없습니다.

이 함수를 인수없이 호출하면 에러가 발생합니다.

> TypeError: Cannot read property 'reduce' of undefined.

이 두가지 점에서 좋지 못한 코드입니다. 함수 사용자는 그 상세한 구현을 모릅니다. 이 에러 메세지는 사용자에게 도움이 되지 않습니다.

이 함수는 기능하지 않습니다만, 만약 다음과 같이 적절한 예외를 던짐으로, 사용자가 실수했다 알 수 있습니다.

> TypeError: Cannot execute function for empty list.

하지만 아마 에러를 던지는 대신 0를 반환하는 설계를 하는 것이 적절할 수도 있습니다.

어찌되었든, 본래대로 있는 것이 아니라 개선을 할 필요가 있겠네요.

Problem #2

그른 입력 대책이 없습니다.

배열이 아니라, 문자열, 수치, Object 등이 들어가면 어떻게 될까요?

sumOddValues(42);

TypeError: array.reduce is not a function

array.reduce은 분명 함수이므로, 이런 메세지는 좀 그럽니다.

인수에 array로 이름 붙였는데, 인수는 어찌 되었든 array라는 이름이 되어버립니다.

에러 메세지가 의미하는 것은, 42.reduce는 함수가 아니라는 것입니다.

이 에러 메세지는 혼란을 조장합니다.

이하와 같은 메세지를 띄우는 것이 현명합니다.

TypeError: 42 is not an array, dude.

문제점 1, 2은 엣지케이스라 합니다.

엣지케이스는 대응방법이 대부분 패턴화 되어있으므로, 대응방법에 고찰을 필요하는 엣지케이스는 그 다지 없습니다.

이하의 예는 어떻습니까?

> sumOddValues([1, 2, 3, 4, 5, -13]) // => still 9

-13가 홀수임에도 불구하고, 이 함수는 9를 반환합니다.

이 함수에는 이 기능이 필요합니까?

이 입력에는 예외를 낼 필요가 있습니까?

음수를 받아들일 필요가 있습니까?

지금처럼 음수를 무시해도 됩니까?

그렇다면 함수명은 sumPositiveOddNumbers가 적절할 것입니다.

상기한 것처럼 어떤 사양을 결정할까는 간단합니다.

중요한 포인트는 그 사양을 명문화 하는 테스트 케이스를 적자 않은 경우, 뒤에 보수담당자에게 음수를 무시하는 것이 의도적인 것인가 버그인 것인가 알 수 없게

그것은 사양입니다. 이름 모를 사람

Problem #3

유효한 모든 케이스가 테스트되어 있지 않습니다.

이 함수에는 처리가 제대로 일어나지 않는, 꽤 단순한 엣지케이스가 존재합니다.

> sumOddValues([2, 1, 3, 4, 5]) // => 11

2는 홀수가 아닌데도, 결과에 포함되어있습니다.

이유는 간단한데, reduce에 제 2인자를 전달하면 accumulator 초기값으로 취급됩니다.

제 2인자를 넘기지 않은 경우, reduce 컬렉션의 처음 값을 accumulator의 초기값으로 합니다.

이로인해, sumOddValues 결과에는 컬렉션의 첫번째 값이 이미 포함되어버립니다.

코드를 적을 때, 이 값을 체크하는 테스트를 쓰지 않았다면, 문제가 바로 발견할 수 있었을 겁니다.

테스트를 최소한밖에 쓰지 않는, 엣지테스트를 쓰지않는다, 등 징조는 초심자라는 증거입니다. 기존의 코드를 의심하지 않는다

당신이 이미 혼자서 일하는 슈퍼스타가 아닌이상, 품질이 좋지 않은 코드를 만날 것입니다.

초보자는 만난 코드의 품질을 신경쓰지 않고, 잘 움직이니까 좋은 코드라 인식해, 자신의 지식으로 만듭니다.

더 나쁜 것은, 그게 좋은 코드라 생각하고 있어서 온갖 장소에 나쁜 코드를 양산합니다.

딱 봐도 나쁜 코드가 있습니다만, 어떤 특별한 이유로인해 계속 그렇게 코드를 쓰던 시기가 있기 마련입니다.

그럴 경우에는 그 코드가 왜 그렇게 쓰여지있는가 주석을 다는 것이 좋은 코드를 구분하는 방법입니다.

초보자라면, 이해할 수 없는데 설명도 없는 나쁜 코드는 나쁜코드이다.

이렇게 생각합시다.

이에 이해 질문하며, git blame 해봅시다.

그 코드의 작성자는 이미 없으며, 어떤 경우에는 내용을 기억조차 못하는 경우 그 코드를 조사해서, 속을 이해합시다.

코드를 완전히 파악한 후에, 그 코드에 대해 좋고 나쁨을 판단합시다.

이해하기 전에 가정을 붙여 추측하면 안됩니다. 베스트 프락티스에 몰두한다

베스트 프락티스는 유해한 단어라 생각합니다.

베스트 프락티스, 즉 이건 이 이상 연구할 여지가 없음, 의문의 가질 틈이 없음 이라는 뜻입니다.

베스트 프락티스라는 건 없습니다.

현 시점에, 그 프로그래밍 언어에 대한 굿 프락티스만 존재할 뿐입니다.

일찍이 베스트 프락티스된 문법의 몇몇은, 현재는 배드 프락티스라 인식되고 있습니다.

충분한 시간을 가하면, 더 좋은 방침을 찾는 것이 될 것입니다.

베스트 프락티스에 몰두하는 걸 그만두고, 베스트를 만드는데 집중합시다.

어디선가 베스트 프락티스를 해라 한다면, 누가 그렇게 하고 있다면, 누가 그렇게 말한다면, 그렇기에 한다.

이런 짓은 하지 말아주세요.

저기에는 제가 이 글에서 밝힌 모든 조언도 포함되어 있습니다.

모든 것을 의심하고, 정석으로 도전하며, 모든 선택지를 조사해, 의식해서 의도를 결정합시다. 최적화에 몰두한다

시기상조한 최적화는, 프로그래밍에서 만악의 근원이다. Donald Knuth

도널드 커누스가 이 발언을 한 이후, 프로그래밍은 크게 변했습니다만, 이 발언의 중요성은 변하지 않았습니다.

하나를 배울 때에는, 병목되는 곳이 어딘가에 있을거라 추측하는 것은 최적화할 필요가 없습니다.

코드를 실행하기 전에, 최적화 하는 것은 시기상조이며, 애초에 최적화가 완전하게 불필요한 가능성도 있습니다.

물론, 새로운 코드를 도입하는 경우 이미 검토해야 할 최적화 항목은 존재합니다.

만약 Node.js라면, 이벤트 루프가 넘치거나, 콜스택을 블록하는 것을 하지 않도록 하는 것이 무척 중요합니다.

이를 염두하면서, 제일 먼저 최적화해야합니다.

구현하는 코드는 콜스택을 블록하는가에 대해 매번 자문해주십시오.

기존의 코드를 보며, 측정을 하지않고 하는 최적화는 유해하며, 피해야 할 일입니다.

퍼포먼스를 올리기 위해 바꿔야하는 것에, 예상 외 버그가 발생할 가능성도 있습니다.

발생하지 않은 퍼포먼스 문제를 최적화 하기 위해서 시간을 소비하는 것은 쓸모없는 행동입니다. 엔드유저 시점으로 보지 않는다

앱에 기능을 추가하는 간단한 방법은 무엇일까요.

자신의 입장으로 생각하면, 기존의 인터페이스에 적합한가 고르는 것이 적절합니다.

그 기능이 유저에서 정보입력을 필요로 하는 경우, 이미 존재하는 폼에 추가합니다.

그 기능이 다른 곳으로의 링크를 추가하는 것이라면, 이미 존재하는 링크 메뉴에 추가합니다.

개발자 시점으로 보지마세요.

자신이 엔드유저라는 것은 시선으로 만사를 봅시다.

그 기능이 사용자가 무엇이 필요로하는가, 유저는 어떻게 행동할까 생각합니다.

필요한 것은 유저가 그 기능을 간단히 발견해 간단히 사용하는 방법이지, 간단히 구현하는 것이 아닙니다. 적절한 도구를 사용하지 않는다

누구나 프로그래밍을 할 때 좋아하는 도구를 몇 가지고 있습니다.

어떤 도구는 멋지며, 어떤 도구는 좋지 않습니다만, 대부분의 도구는 어떤 분야에 강력하지 다른 분야에서는 그렇지 않습니다.

망치는 못을 벽에 박을 때는 좋은 도구지만, 나사를 돌릴 때는 최악의 도구입니다.

망치가 너무 좋아도, 그것으로 나사를 박으면 안됩니다.

Amazon 유저리뷰가 5.0여도 그러합니다.

사용할 도구는 인기로 고르는 것이 초보자인 증거입니다.

그 원인의 하나의 이유는 그 일에 더 적합한 도구인가 모르기 때문이지요.

당신은 지금 사용하고 있는 도구는, 당신이 지금 알고 있는 것중에서 제일 좋은 도구일지도 모르겠습니다만, 하지만 결코 모든 도구 중 가장 좋은 것을 아닙니다.

사용하는 도구에 손을 떼서, 새로운 도구를 시작해 볼 필요가 있습니다.

일부 코더는 새로운 도구 사용을 거부합니다.

그들은 기존의 도구에 익숙해져서, 새로운 도구를 습득하고 싶지 않을 것입니다.

마음은 이해합니다만, 하지만 그 태도는 단순하게 말하면 잘 된 것입니다.

당신은 원시적 도구에 시간을 낭비하면서 집을 세울 수도, 좋은 도구에 돈과 시간을 투자한 후에 단기적으로 더 좋은 집을 세울수도 있습니다.

도구는 끊임없이 개선되며, 그것에 대해 끊임없이 학습해, 사용하는 것이 필요합니다. 코드 문제가 데이터 문제로 파생된다

프로그램의 중요한 일 중 하나는, 모든 데이터의 관리입니다.

프로그램은 새로운 코드를 추가하거나, 낡은 레코드를 삭제하거나, 레코드를 변경하는 인터페이스입니다.

프로그램이 버그는 정말 사소해도, 치명적으로 예측불가능한 데미지를 데이터에 줄 수 있습니다.

모든 데이터가 버그가 있는 프로그램을 지나갔다면, 사태는 더 심각합니다.

초보는 코드와 데이터의 관계성을 묶는 것이 어려울지도 모르겠습니다.

그 기능은 현재 동작하지도 않으면서, 중요하지 않으므로, 무의식적으로 버그가 들어가 코드를 계속 쓸 수도 있습니다.

문제는 버그가 들어가 코드가 데이터의 안정성을 조용히 파괴할 수 있다면 가능성입니다.

더 나쁜 것으나, 데이터에 일어난 버그에 대응하지 않고 코드의 버그만 수정하는 것입니다.

그 코드는 작은 데이터의 문제를 증폭시켜, 결국 회복불가능한 수준가지 데이터를 파손시킵니다.

이런 문제에서 데이터를 지키기 위해서는?

예를 들면 데이터의 정합성을 검토할 수 있는 복수의 레이어를 사용할 수 있습니다.

하나의 레이어로는 정합성을 보장할 수 없습니다.

프론트엔드, 백엔드, 네트웤, 여기에 데이터베이스 레이어에 검증을 넣어봅시다.

이게 어렵다면, 최소한 데이터베이스 레벨의 제약은 사용하면 안됩니다.

데이터베이스 제약에 대해 이해하며, 테이블이나 열을 추가하는 것에 사용할 제약을 반드시 사용합시다.

NOT NULL

NOT NULL 제약은, 그 열에 대해, NULL값 설정을 금지합니다.

앱이 그 필드 값을 필수로 한다면, 데이터베이스에는 NOT NULL제약을 넣어야합니다.

UNIQUE

UNIQUE 제약은 그 열에 테이블 전체에 중복하는 값을 갖지 않도록 하는 제약입니다.

예를 들면 유저테이블의 유저명, 메일주소 같은 곳에 지정하면 좋겠네요.

CHECK

CHECK 제약은 그 식을 충족하지 않은 데이터를 받지 않는 것입니다.

예를 들어, 값이 0이상 100 이하가 아니라면, CHECK 제약을 사용하므로 이를 강제할 수 있습니다.

PRIMARY KEY

PRIMARY KEY는 열의 값이 NULL이 아닌 유니크인 경우를 보여줍니다.

아마 이는 사용하고 계시겠지요.

데이터베이스 안에 각 테이블에는 레코드를 식별하는 것의 PRIMARY KEY가 필요합니다.

FOREIGN KEY

FOREIGN KEY는 열의 값이 다른 테이블의 열, (통상적으로 PRIMARY KEY) 와 일치하지 않으면 안된다를 의미합니다.

초보자 범하기 쉬운 데이터 정합성에 관한 하나의 문제는 트랜잭션이라는 생각의 결여입니다.

복수의 조작이 서로 의존해서 데이터를 변경한다면, 그 조작의 하나가 실패할 경우 모든 것을 처음으로 돌리기 위해 트랜잭션을 사용할 필요가 있습니다.

바퀴의 재발명

이건 어려운 부분입니다.

프로그래밍은 지금도 여전히 모든 것을 알고 있는 분야가 아니며, 어떤 바퀴에는 재발명할 가치가 있기도 합니다.

모든 것이 꽤 빨리 변화하며, 새로운 요소가 점점 유입됩니다.

예를 들어, 현재 시간대보다 잘 돌아가는 바퀴가 필요한 경우는 이미 존재하고 있는 바퀴를 커스터마이즈하는 것이 아니라, 그냥 바퀴를 재발명할 필요가 있습니다.

하지만 원형을 하지 않는 것처럼, 기존설계를 벗어난 바퀴는 그것을 어떻게 보더라도 반드시 그것을 써야한다 하는 것이 아닌 이상 재발명하지 말아주세요.

많은 선택지에서 최적한 브랜드의 바퀴를 선택하는 것은 무척 어렵습니다.

구입하기 전에 여러 선택지를 테스트해보세요.

소프트웨어의 좋은 점은, 많은 것이 무료이며, 바퀴 내부를 직접볼 수 있다는 것이다.

바퀴의 품질은 내부 코드 설계를 보는 것으로 쉽게 판단할 수 있습니다.

가능하다면, 오픈소스 바퀴를 사용해주십시오.

오픈소스는 간단히 패키지를 수정하는 것이 가능하면, 간단히 교환하는 것도 되는데다가, 여기에 커뮤니티의 서포트도 있습니다.

또한 바퀴가 필요한 경우는, 차를 1대 구입하는 것이 아니라, 이미 가지고 있는 차에 바퀴만 바꾸시길 바랍니다.

함수 하나, 둘을 사용하기 위해서, 라이브러리 전체를 도입하지 마세요.

이 것으 관핸서는 적절한 예시가 JavaScript의 Lodash입니다.

배열을 셔플하기 위해서는 shuffle 메서드만 쓰면 되지, lodash 라이브러리 전체를 임포트하면 안됩니다.

역주

import lodash from "lodash";
import { shffule } from "lodash";
import shuffle from "lodash/shffule";
import shuffle from "lodash.shuffle";

1번은 너무 크고, 4번은 패치에 따라 어떻게 변할줄 모른다.

되도록 3번을 고르며, 이렇게 하면 적은 번들사이즈로 만들 수 있다.

초보자 티가 나는 징후 중 하나는 코드리뷰를 비판/비난으로 여기는 것입니다.

그들은 그걸 좋아하지도, 감사하지요 않습니다, 오히려 무서워하죠.

이건 잘 못된 것입니다.

그렇게 느끼셨다면, 빨리 태도를 바꿀 필요가 있습니다.

모든 코드리뷰는 학습의 계기라 생각해주세요.

그것 환영하고, 인정해 그것을 배웁시다.

그리고 가장 중요한 점은 리뷰어에게 감사하자는 것입니다.

당신은 영원히 코드를 배워야합니다.

그것을 의식해주세요.

대부분의 코드리뷰는 당신이 모르는 것을 알려줍니다.

학습기회라 여깁시다.

가끔 리뷰어가 실수해서 당신도 그들에게 무엇인가 알려줄지도 모릅니다.

단, 당신의 코드만으로 어디가 나쁜지 명백히 알 수 없다면, 코드를 수정할 필요가 있습니다.

리뷰어에 대해 무엇인가 배울 기회가 있다면, 그건 프로그래머에게 무척 유익한 활동중 하나입니다. 버전 관리하지 않는다

초보자는 버전관리 시스템의 힘을 모릅니다.

여기는 Git을 예로 듭니다.

버전관리는 단순히 다른 사람의 코드에 변경을 더해 push하는 도구가 아닙니다.

버전 관리는, 즉 역사입니다.

코드에 의심이 생겼을 때, 그 코드의 역사는 의문을 해결해줄 때 도움이 됩니다.

그러므로 커밋 메세지도 중요합니다.

그것은 당신의 구현이 무엇을 위해 있는지 표현하는 하나의 수단입니다.

커밋은 되도록 소규모로 하면, 미래의 관리자가 코드가 왜 그런 상태가 되었는지 조사할 때 도움이 됩니다.

빈번하고 짧게 커밋하며, 커밋 메세지는 현재형을 사용합시다.

커밋 메세지에 요약을 자세히 기술합시다.

메세지가 수행이상 필요하다면, 그 커밋이 너무 크다는 증거이므로 rebase합시다.

커밋 메세지에는 불필요한 것은 넣지맙시다.

예를 들어 추가, 변경, 삭제된 파일명 리스트는 필요없겠죠.

그것은 커밋자체에 들어가서, 커맨드로 간단히 볼 수 있으므로, 소음에 불과합니다.

또 파일 마다 다른 커밋 메세지를 붙이려는 분도 있겠습니다만, 그것은 아마 커밋이 크다는 징후 중 하나입니다.

버전관리는 도착가능성에 관한 것입니다.

어떤 함수의 설계, 필요성에 의문을 느낄 때, 그 함수가 도입된 커밋을 발견해서 당시의 상황을 알 필요가 있습니다.

커밋은 또 프로그램에 버그가 난입된 타이밍을 특정하는데 도움이 됩니다.

bisect 커맨드를 사용해 버그가 들어간 커밋을 이진탐색으로 특정 것도 가능해집니다.

버전관리는 변경을 공식으로 받아들인다는 용도가 아닌 곳에서도 대활약합니다.

스테이징 환경 구축, 패치 선택, 수정의 리셋, 보류, 커밋의 수정, 차이 확인, 되돌리기 등등 코드를 다루는데 풍부한 기능이 있는 도구가 생기는 것입니다.

이것을 이해해, 학습하며, 사용합시다.

일고 있는 Git의 기능이 적은 사람은 초보자에 가깝다 할 수 있습니다. 글로벌의 과용

여기서는 함수형 프로그래밍과 그 이외의 패러다임의 차이를 말하는 것이 아닙니다.

그건 다른 글이 있습니다.

여기서는 글로벌한 것은 만약의 원흉이라는 것, 가능하다면 무조건 피해야한다는 것이라는 사실을 말할 것입니다.

만의 하나 그것이 불가능한 경우라면, 글로벌를 사용은 필요한 최저로 눌러주시길 바랍니다.

제가 초보자가 일 때 의식하지 못한 것 중 하나로, 정의한 모든 변수가 공유상태에 놓여있었다는 것입니다.

다시 말하면, 그 변수는 그 변수와 같은 스코프에 있는 모든 요소에서 조작가능하다는 뜻입니다.

스코프가 글로벌에 가까울수록, 그 공유상태의 치안이 악화됩니다.

가능하다면 작게 스코프를 가지며, 밖으로 유출시키지 마세요.

복수의 리소스가 동시에 하나의 변수를 조작하면, 글로벌은 큰 문제가 생깁니다.

즉, 경합상태입니다(Race Condition).

초보자는 경합상태, 특히 데이터 잠금에 문제의 회피책으로 타이머를 사용하는 경향이 있습니다.

그건 위험신호입니다.

하지마세요.

코드리뷰에서 지적할 일이고, 막을 일입니다. 에러를 싫어한다

에러는 좋은 것입니다.

코드가 전진한다는 의미죠.

즉, 개선의 추적을 간단히 할 수 있습니다.

프로 개발자는 에러를 사랑하지만, 초보는 그렇지 않습니다.

에러가 마음에 들지 않는다면, 에러를 향한 태도를 바꿀 필요가 있습니다.

에러는 힌트이며, 대처이며, 활용한다 생각합시다.

어떠한 에러는 예외적으로 개선하는 필요가 있습니다.

예외는 유저가 정의하는 에러의 하나입니다.

또 다른 몇 에러는 그대로 할 필요가 있습니다.

그것은 앱을 크래시해서 강제종료합니다. 휴식을 하지 않는다면

당신은 인간입니다, 뇌에는 휴식이 필요합니다.

몸도 휴식이 필요합니다.

당신은 점점 자신만의 영역에 들어가, 먹고 자는 것조차 잊으며 집중하기도 할 것입니다.

저는 이것이 초보라는 증거라 생각합니다.

이것은 누가 대신해줄 수 있는 것이 아닙니다.

일의 흐름에 휴식을 넣어야합니다.

짧은 휴식을 많이 취해, 책상에서 떨어져 주변을 걷고, 다음에 무엇을 할지 생각합시다.

심기일전해서 코드로 돌아옵시다.

> "엘리트 코더가 다른 코더보다 뛰어난 능력을 발휘하는 방법"

코더가 아닌 엔지니어가 될 것 (Be an engineer, not a coder)

> 엔지니어링은 문제를 해결하는 것 > 최고의 엔지니어는 코드를 목적 달성을 위한 수단으로 생각함 > 코드를 작성하는 즐거움은 있지만 목적이 없는 코드를 작성하는 것은 의미가 없음. 대신 코드는 사용자를 위한 솔루션을 설계하는 데 사용됨 > 코딩은 창의성을 추구함! 창의력은 제약 조건 하에서 많이 발생함. 해결해야 할 명확한 문제라는 '제약'을 추가하면 엔지니어는 자신이 적합하다고 생각하는 방식으로 솔루션을 탐색하고 만들 수 있는 자유를 누릴 수 있음 > 최고의 엔지니어들은 제품 중심적이며, 무엇보다도 사람을 위한 문제 해결을 최우선으로 생각하며, 이는 다음 단계로 이어짐

컴퓨터가 아닌 인간을 위한 코드 (Code for the human, not the computer)

> "바보라도 누구나 컴퓨터가 이해할 수 있는 코드를 작성할 수 있습니다. 훌륭한 프로그래머는 인간이 이해할 수 있는 코드를 작성합니다." - 마틴 파울러 > 코드는 컴퓨터뿐만 아니라 인간을 위한 것 > 코드는 그 코드를 읽고, 유지 관리하고, 당신의 코드 위에 빌드하는 같은 팀 엔지니어를 위한 것 > 코드는 휴대전화를 사용하는 어린아이, API를 호출하는 개발자, 본인 등 사용자를 위한 것 > 최고의 엔지니어들은 항상 모든 사용자를 위해 코드의 가치를 평가했음 > 만약 그들이 고객 중 한 명이라도 만족시키지 못하면 그 코드는 프로덕션에 적용되지 못했음

코드 자체에서 벗어나기 (Detach from the code itself)

> 뛰어난 엔지니어는 코드 자체에 집착하지 않음 > 그들은 최종 결과물이 전반적으로 더 좋아질 수 있다면 90% 정도 완성된 코드라도 삭제하고 다시 시작하는 것을 두려워하지 않음 > 코드는 개인적인 것이 아니기 때문에 피드백을 적극적으로 받아들임 > 코드는 완벽하지 않음. 아무도 완벽한 코드에 관심을 두지 않음. 변화를 가져오는 코드에 관심이 있을 뿐 > 코드에 집착하지 않도록 스스로를 가르치는 가장 좋은 방법은 "20년 후에는 당신 코드의 대부분이 기술 부채가 되거나 더 이상 사용되지 않거나 다시 작성될 가능성이 높다는 것을 깨닫는 것"

일관된 표준 사용 (Use consistent standards)

> 코드를 작성할 때 일관된 표준과 코딩 스타일을 고수할 것 > 일관성(Consistency)을 유지하면 미래의 당신과 팀원 모두가 코드를 더 쉽게 읽고 이해할 수 있게 해줌 > 일관된 스타일 가이드를 사용하면 팀과 코드베이스 모두 더 쉽게 확장할 수 있음 > 이게 Meta/Google 같은 회사가 시간이 지나도 코드베이스를 읽을 수 없거나 유지 관리할 수 없게 되는 일 없이 많은 코드를 신속하게 배포하는 방법 > 모든 뛰어난 성과를 내는 기업은 스타일 가이드를 내재화하고 그 이점을 알고 최대한 이를 따랐음 > 구글은 일부 스타일 가이드를 오픈소스로 공개했음 > 메타는 그들의 일부 오픈소스에 C++ 스타일 가이드가 있음 > 팁: 당신의 팀을 위해서 Linter를 포맷팅하는 것은 확실히 시간을 들여 설정할 가치가 있음

간단한 코드를 작성 (Write simple code)

> 내가 아는 모든 엘리트 엔지니어는 생성하기엔 복잡했을 수도 있지만, 결국엔 읽고 이해하기 쉬운 코드를 생성했음 > 이에 대해 내가 한 가장 좋은 말은 그들의 코드가 "미학적으로 만족스럽다는 것" > 그들의 코드는 깔끔하고, 체계적이며, 논리적(clean, organized, and logical) > 코드에서 내린 각 결정은 의미가 있었고, 그렇지 않은 경우엔 코드내에 잘 문서화 되어 있었음 > 깔끔한 코드를 작성하는 좋은 방법은 SOLID 원칙과 같은 원칙을 따르는 것. 처음에는 OOP(객체 지향 프로그래밍)를 염두에 두고 설계되었지만 일반 프로그래밍에도 확장할 수 있음 > Single Responsibility: 한 클래스는 하나의 책임만 가져야 함 > Open-Closed: 소프트웨어 객체(클래스, 모듈 등)는 확장에는 개방적이지만 수정에는 폐쇄적이어야 예측 가능하고 유지 관리 가능한 코드를 만들 수 있음 > Liskov Substitution: 하위 유형은 프로그램의 정확성에 영향을 주지 않으면서 기본 유형으로 대체할 수 있어야 함 > Interface Segregation: 코드가 모든 것을 사용하지 않는 거대한 인터페이스에 종속되어서는 안 됨. 대신 패키지는 더 작은 특정 인터페이스를 포함하고 임포트할 수 있도록 허용해야 함 > Dependency Inversion: 상위 레벨 모듈이 하위 레벨 모듈에 종속되어서는 안 되며, 둘 다 추상화에 종속되어 보다 유연하고 분리된 시스템 설계를 촉진해야 함 > 그 예로 이름 짓기(Naming)를 들 수 있음: 좋은 이름에는 마법의 값이 없고, 구분이 명확하며, 설명적인 함수 이름과 이해하기 쉬운 변수를 표현함

의외성을 허용하지 않음 (Don’t allow surprises)

> 코드는 예상치 못한 결과를 만들어서는 안 됨. 이는 코드 원칙을 따르고 적절한 테스트를 작성함으로써 가능 > 좋은 코드는 예측 가능함 > 테스트는 코드의 명확성과 예측 가능성을 강화하고, 자신감을 줌. 우수한 자동화된 테스트를 통해 팀은 보이지 않는 부분을 깨뜨릴 염려 없이 코드를 변경할 수 있음 > 몇가지 테스트 유형 > 개별 컴포넌트 및 격리된 함수에 대한 단위 테스트 > 여러 컴포넌트 간의 상호 작용을 위한 통합 테스트 > 사용자 관점에서 전체 시스템의 기능을 평가하는 엔드투엔드 테스트 > 테스트는 간단해야 함. 실패한 테스트를 읽었을 때 무엇이 잘못되었는지 쉽게 파악할 수 있어야 됨 > 테스트하지 말아야 할 항목을 아는 것도 중요 > 예를 들어, 엔드투엔드 테스트의 수고가 프로그램의 실제 이점보다 크다면 테스트는 신중한 문서화, 모니터링 및 적절한 사람(예: 코드 소유자)에게 알림을 보내는 것으로 대체 > 또한 테스트는 프론트엔드 코드에서 특정 CSS 선택기를 테스트하는 것과 데이터 속성 또는 스크린샷 테스트만을 사용하는 것과 같이 코드 내의 구현 세부 사항을 테스트해서는 안 됨

자주 소통하기 (Communicate often)

> 훌륭한 시스템은 혼자서 만들어지지 않음. 훌륭한 엔지니어들은 설계 검토를 거치고, 피드백을 요청하고, 코드에 대한 초기 설계를 계속 반복했음 > 누구나 자신의 지식에는 다른 사람이 채워줄 수 있는 빈틈이 있음. 새로운 관점은 종종 코드를 더 명확하게 만들거나 이전에는 생각하지 못했던 새로운 접근 방식을 제공하는 데 도움이 될 수 있음 > 최고의 엔지니어들은 더 나은 최종 결과물을 얻기 위해 시간을 들여 함께 작업하는 것을 두려워하지 않고 소통과 협업을 중시 > 문서에 대한 빠른 검토를 위해 팀원에게 핑을 보내거나 중요한 풀 리퀘스트에 코드 검토자를 추가하는 것처럼 간단하게 할 수 있음

빠르게... 그리고 느리게 코딩하기 (Code fast… and slow)

> 내가 아는 최고의 엔지니어들은 프로젝트를 빠르게 완료하지만 코딩은 느리게 함 > 이상하게 들리죠? > 위의 모든 원칙과 습관은 코딩의 첫 번째 단계에 더 많은 시간을 추가함. 하지만 이러한 원칙과 습관을 통해 엔지니어는 프로젝트의 진행 상황을 한 단계씩 발전시킬 수 있음 > 표준을 사용하고, 제대로 테스트하고, 원칙을 사용하고, 자주 소통하는 데 시간을 할애함으로써 장기적으로 더 많은 시간을 절약할 수 있음 > 인턴과 주니어 엔지니어 시절에 제가 직접 경험했고, 다른 많은 엔지니어들도 마찬가지겠지만, 3보 전진을 서두르다 장애물에 부딪혀 5보 후퇴하게 될 수 있음

맹목적으로 규칙을 따르지 말 것 (Don’t follow rules blindly)

> 위의 '규칙'과 '원칙'은 단순한 가이드라인일 뿐. 모든 것이 가이드라인에 깔끔하게 잘 들어맞는 것은 아님 > 때로는 작성 중인 코드가 원 안에 들어가지 않는 정사각형일 수도 있지만 괜찮음 > 이 경우 코드가 특정 방식으로 작성된 이유를 문서화할 것 > 그렇지 않으면 미래의 여러분과 같은 누군가가 나중에 코드를 보고 "와, 그때는 내가 멍청했었지. 왜 이게 우리 표준을 따르지 않는 거지?"라고 생각할 수 있음 > 그러면 그들은 이전과 같은 결론에 도달하기 위해 20시간 동안 표준에 맞게 다시 코딩하는 데 시간을 소비할 것 > 소프트웨어 개발의 현실은 모든 코드가 깔끔하거나 규칙을 완벽하게 따를 수 없다는 것 > 하지만 일관성 있고, 깔끔하고, 이해하기 쉽고, 테스트할 수 있고, 가치 있는 코드는 존재할 수 있음 > 최고의 엔지니어에게서 발견한 또 다른 패턴들 > 적어도 한 가지 분야에 대한 깊은 도메인 지식을 가짐. 제가 기록했던 모든 엔지니어는 프론트엔드 인프라, 분산 시스템, 깔끔한 UI 등 특정 분야에 집중하여 전문가가 되었기 때문에 현재 해당 분야에서 최고의 자리에 올랐음 > 자신을 자주 그리고 적절하게 마케팅함. 이 엔지니어들은 눈에 잘 띄지 않는 곳에 숨어 있지 않았음. 팀원들과 함께 일하는 모든 사람들이 자신의 가치와 전문성을 알고 있었고, 이는 적절한 마케팅과 영향력 있는 프로젝트를 수행한 결과

규동 기본 재료

- 얇게 썬 소고기 (샤브샤브용 또는 우삼겹) 160g~200g - 양파 1/4개 ~ 1개 (채 썰기) - 쪽파 또는 대파 약간 (송송 썰기) - 계란 노른자 (선택 사항) - 밥

규동 소스 재료

간장 1~3 큰술

맛술 또는 미림 1~2 큰술

청주 1 큰술 (optional)

설탕 1 큰 술

쯔유 25ml (또는 시판 쯔유 대체 가능)

물 100~200ml

생강가루 약간

다진 마늘 1 작은 술 (optional)

조리법

- 팬에 식용유를 두르고 얇게 썬 소고기를 살짝 볶다가 채 썬 양파를 넣어 투명해질 때까지 볶는다. - 물, 간장, 설탕, 맛술, 쯔유, 청주, 생강가루 등을 섞어 만든 소스를 부어 센 불에서 졸이듯 끓인다. - 밥 위에 소고기와 양파 소스를 올리고, 송송 썬 쪽파와 계란 노른자를 얹어 마무리한다. - 노른자를 터뜨려 섞어 먹으면 부드럽고 감칠맛이 더욱 살아난다.

수란(온천달걀)을 집에서 정확하고 부드럽게 만드는 방법은 다음과 같다.

준비물

- 달걀 (상온, 1시간 이상 꺼내놓기) - 물 (끓는 물과 상온 물을 섞음) - 냄비와 뚜껑

만드는 방법

- 냄비에 물 7컵을 끓인다. - 물이 끓기 시작하면 불을 끄고 상온 물 3컵을 부으면 대략 74~75도 정도의 온도가 된다. - 상온에 둔 달걀을 깨지지 않게 조심스럽게 넣는다. - 냄비 뚜껑을 덮고 16분간 그대로 둔다(뚜껑 덮기 필수). - 시간이 지나면 달걀을 꺼내어 실온에서 5분간 식힌다.

Wired 창립 편집장 케빈 켈리(KK)가 올해 68세 생일날 블로그에 올린 훌륭한 조언들 간단 번역

왜 잘못된 일이 벌어지는가 Scale AI CEO Alexandr Wang의 메모

Scale AI의 CEO, Alexandr Wang이 2019년 Scale AI 팀에게 보낸 메모 중 일부

정보 압축: 왜 일이 잘못되는가?

  1. 정보 압축이란 무엇인가?
  1. 왜 정보 압축이 문제를 일으키는가?
  1. 조직 규모와 정보 압축의 상관관계
  1. 정보 압축 문제의 대표적 사례
  1. 정보 압축의 해법
  1. 핵심 요약

왜 사실만으로는 생각이 바뀌지 않는가 — "구조가 답이다" (vasily.cc)

사실이 아니라 구조가 신념을 결정

신념 구조(그래프)의 예시

구조적 공격—노드·엣지의 흔들림

인간 심리와 신념 구조

구조적 경쟁과 실제 사례

조직적 허위 행동(coordinated inauthentic behavior)

우리의 신념 구조를 어떻게 지킬 것인가

결론

게임 엔진 없이 비디오 게임 개발하기 (2025) (noelberry.ca)

서문: 20년차 게임 개발자의 소회

게임 개발에 필요한 프로그래밍 언어 선택

크로스플랫폼 라이브러리와 렌더링·입력 구현

에셋(Assets) 처리 방법

레벨 에디터 및 UI 툴

크로스플랫폼 및 콘솔 이식성

개발 환경: Linux 중심의 오픈 생태계

추가 Q&A 및 사례

결론

2025년 셀프 호스팅 가이드

컨테이너 런타임

웹 기반 컨테이너 관리 도구

리버스 프록시와 VPN

상태 모니터링 및 알림 도구

- Uptime Kuma - Zabbix나 Grafana 같은 무거운 스택 없이도 간편하게 서비스 상태 모니터링 가능 - 단 한 번의 배포로 다양한 채널을 통한 알림 설정 가능 - 셀프 호스팅 시스템의 가용성 체크에 탁월함 - Gotify - 간단한 HTTP 요청으로 푸시 알림 전송이 가능한 알림 서버 - 모바일 앱 설치 후 알림 수신 가능하며, Uptime Kuma 등과의 통합도 가능 - 다양한 셀프 호스팅 툴과 함께 사용할 수 있어 실용적임

셀프 호스팅 앱 검색을 위한 추가 자료

- 다양한 셀프 호스팅 애플리케이션을 찾을 수 있는 유용한 자료 모음임 - Awesome-Selfhosted - GitHub에서 제공하는 방대한 소프트웨어 리스트 - 다소 정리되지 않았고, 개발이 중단된 프로젝트도 일부 포함되어 있음 - Selfh.st - 작성자가 가장 좋아하는 자료 출처임 - 틈새 애플리케이션 추천 뉴스레터와, 정렬 기능이 있는 대형 애플리케이션 디렉토리를 제공함 - 매우 유용한 사이트임 - awesome-docker-compose - 즉시 실행 가능한 Docker Compose 파일 모음 - 일부 애플리케이션은 업데이트로 인한 호환성 문제를 가질 수 있으나, 전반적으로 좋은 참고 자료임

SPF, DKIM, DMARC 가이드

이 가이드는 무엇을 위한 것인가?

왜 이 가이드를 선택해야 하는가?

이 가이드는 무엇을 위한 것이 아닌가?

SPF, DKIM, DMARC 간단히 설명하기

SPF (Sender Policy Framework)

DKIM (DomainKeys Identified Mail)

DMARC (Domain-based Message Authentication, Reporting & Conformance)

SPF, DKIM, DMARC의 실제 사용 예시

이제 무엇을 해야 할까?

SPF, DKIM, DMARC 상태 확인

FAQ's with SPF, DKIM and DMARC

마무리

GN⁺의 의견

케빈 켈리의 알았더라면 좋았을 103가지 조언 (kk.org)

ABC 원칙의 일상 적용

6-C 체계적 접근법

<img src="image.jpg" fetchpriority="low" loading="lazy" />

CSS에서 시스템 폰트 사용하기

body {
  font-family:
    -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
    Arial, sans-serif;
}

body {
  font-family:
    -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial,
    sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
}

:root {
  --system-ui:
    system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif,
    "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
}

.element {
  font-family: var(--system-ui);
}

과목에서 좋은 성적 받기: Andrej Karpathy의 성공을 위한 조언 (cs.stanford.edu)

과목에서 좋은 성적을 받기 위한 Andrej Karpathy의 가이드

일반 원칙

시험 준비

시험 당일

- 최적의 식사/음료 습관은 시험 2시간 전 커피와 음식 섭취 - 시험 직전 커피나 음식은 항상 나쁨 - 잠재적으로 스트레스가 많은 상황 직전의 커피는 항상 나쁨 - 커피를 전혀 안 마시는 것도 나쁨 - 시험 직전에 매우 집중적으로 공부: 많은 사람들이 시험 전에 포기하고 "휴식"을 취한다고 주장 - 단기 기억은 훌륭한 도구이므로 낭비하지 말 것 - 시험 직전까지 최대한 집중적으로 공부 - 휴식이 필요하면 시험 1시간 전에 취하되, 시험 30-45분 전에는 정말 열심히 공부

시험 중

- 항상 연필 사용: 쓰레기 같은 "솔루션"을 지울 수 있어야 함 - 시작 전 모든 문제를 매우 간단히 훑어보기: 문제당 1-3초 정도만 보면 충분 - 핵심 단어를 흡수하고 전체 시험의 규모를 파악 - 쉬운 문제부터 먼저 풀기: 한 문제에 너무 오래 붙잡혀 있지 말고 나중에 다시 돌아오기 - 첫 번째 pass에서 시험의 30%만 완성하는 경우도 있음 - 일부 문제는 "워밍업" 후에 훨씬 쉬워짐 - 시험지를 항상 깔끔하게: 사람이 채점한다는 명백한 사실을 깨닫는 사람이 놀랍게도 적음 - 슬픈 사람은 낮은 점수를 줌 - 항상 답을 박스/원으로 표시: 특히 주변에 유도 과정이 있을 때 - 채점자가 빠르게 체크 표시하고 넘어갈 수 있게 함 - 채점자의 마음가짐을 갖기 - 절대, 절대, 절대 시험을 일찍 떠나지 말 것: 실수를 했을 것이므로(보장함) 찾아서 고치기 - 찾을 수 없으면 시간이 다할 때까지 더 열심히 찾기 - 실수가 없다고 매우 확신하면 시험지를 더 읽기 쉽고 채점하기 쉽게 만들기 - 일찍 떠나는 사람들은 어리석음 - 잠재적 이익이 비용을 완전히 압도하는 상황 - 채점자와 소통: 자신이 적은 것보다 더 많이 안다는 것을 보여주기 - 특정 단계를 할 수 없어도 그 단계를 했다면 어떻게 진행할지 명확히 하기 - 필요할 때 메모를 남기는 것을 두려워하지 말 것 - 채점자들은 종종 더 많은 점수를 찾으려고 노력하므로 쉽게 만들어주기 - 문제당 배점 고려: 많은 시험이 각 문제가 몇 점인지 알려줌 - 무언가 잘못하고 있을 때 매우 강력한 힌트를 줌 - 어떤 문제에 집중해야 할지 강력한 힌트 제공 - 배점이 적은데 상대적으로 어려운 문제에 너무 많은 시간을 쓰는 것은 어리석음 - 5분 미만 남았고 여전히 어떤 문제에 막혀 있다면 중단: 모든 문제를 다시 읽으며 2차 질문을 놓치지 않았는지, 모든 것에 답했는지 확인하는 것이 더 나음 - 사람들이 이런 식으로 잃는 어리석은 점수가 얼마나 많은지 믿을 수 없을 것

가장 중요한 조언

- 학부생들은 수업에 대한 터널 비전을 가지는 경향이 있으며 좋은 성적을 원함 - 깨달아야 할 중요한 사실: 아무도 당신의 성적에 관심이 없음(나쁘지 않은 한) - 가장 똑똑한 학생은 모든 과목에서 85%를 받아 약 4.0 학점을 얻지만 과도하게 공부하지도, 부족하게 공부하지도 않음 - 시간은 귀중하고 제한된 자원: 시험에서 망치지 않을 정도로만 하고 훨씬 더 중요한 노력으로 주의를 전환 - 실제 실무 경험이 극도로 중요: 실제 코드베이스, 프로젝트, 어리석은 과정 연습 외의 문제 작업 - 당신을 알고 좋은 추천서를 써줄 수 있는 교수/사람들이 극도로 중요 - 당신이 주도성, 열정, 추진력을 가지고 있다고 말하는 추천서가 중요 - 취업을 생각 중이라면 여름 인턴십: 대학원 진학을 생각 중이라면 연구 경험을 얻기 - 학교가 제공하는 프로그램에 등록하거나 교수/대학원생에게 연락하여 좋아하는 연구 프로젝트에 참여하기 - 잘 알려진 교수가 당신이 추진력 있고 독립적 사고자라고 쓴 추천서는 다른 모든 것, 특히 성적 같은 사소한 것을 완전히 압도함 - 지원 전에 최소한 한 편의 논문을 끼워 넣으면 많은 도움이 됨 - 프로젝트에 등록한 후 몇 번 만나고 질문을 많이 하다가 갑자기 포기하고 사라지는 학부생이 되지 말 것: 평판을 손상시킴 - 사이드 프로젝트에서 그룹과 함께 참여하거나 처음부터 자신의 프로젝트 시작: 오픈소스에 기여하고, 라이브러리를 만들거나 개선 - 멋진 것을 만들고 잘 문서화하며 블로그 작성 - 몇 년 후 사람들이 관심을 가질 것: 성적은 그저 처리해야 할 골칫거리일 뿐


장치산업이면 PBR 2이내(반도체, 석유화학, 자동차 등등),  소프트웨어같은 무형자산의 기술 기업이라면 PBR은 거의 의미없다고 보고, 그리고 젤 중요하게 보는게 PER 입니다.  PER을 볼때는 현재뿐만 아니라 미래에 회사가 벌어들일수 있는 금액을 대충 예상해봐서 따져봐야 합니다. 단, 미래에 년간 회사가 벌어들일수 있는 돈은 회사말만 믿으면 안되고 나름대로 그 회사가 몸담고 있는 시장 규모를 먼저 보시고, 대충 마켓쉐어 + 이익률 정도로 따져보면 대강의 숫자가 나오는데 이걸로 판단하면 됩니다.  참고로 미국 주식의 역사적 PER range 는  5 ~ 25 사이입니다.  지금 PER 70, 200, 400 씩 하는 유명한 주식들이 아무리 미래 가치를 반영했다 하더라도 너무 말이 안되게 비싼 주식들이 많거든요. 회사에서 말하는 미래 매출, 가치가 그대로 구현되는 회사가 얼마나 될까요? 장이 좋을때는 그럴듯 해 보이지만, 장이 안좋을때는 다 빨가벗게 되어 있습니다. 그런면에서 삼전은 PER나 PBR로 봐도 고평가 된 회사라고 보기는 어렵습니다. 반면 하닉은... 한번 살펴보시기 바랍니다. 삼전이랑 비교해 보시면 됩니다.


  1. 최고의 엔지니어는 사용자 문제 해결에 집착함

    • 기술에 먼저 빠져 적용처를 찾는 것은 유혹적이지만, 가장 큰 가치를 창출하는 엔지니어는 역으로 작업함 - 사용자 문제를 깊이 이해하고 거기서 솔루션을 도출
    • 사용자 집착이란 지원 티켓에 시간 투자, 사용자와 대화, 사용자의 어려움 관찰, 근본에 도달할 때까지 "왜"를 질문하는 것을 의미함
    • 문제를 진정으로 이해하는 엔지니어는 종종 우아한 솔루션이 예상보다 단순함을 발견함
    • 솔루션부터 시작하는 엔지니어는 정당화를 찾기 위해 복잡성을 구축하는 경향이 있음
  2. 옳은 것은 쉽지만, 함께 옳음에 도달하는 것이 진짜 업무

    • 모든 기술 논쟁에서 이기고도 프로젝트를 잃을 수 있으며, 항상 방에서 가장 똑똑한 사람이 되어 조용한 반감을 축적하는 뛰어난 엔지니어들을 목격함
    • 비용은 나중에 "불가사의한 실행 문제"와 "이상한 저항"으로 나타남
    • 핵심 기술은 옳은 것이 아니라 문제 정렬을 위해 토론에 참여하고, 타인을 위한 공간을 창출하며, 자신의 확신에 회의적 태도를 유지하는 것
    • 강한 의견, 약한 집착 - 확신이 부족해서가 아니라, 불확실성 하의 결정을 정체성에 결합해서는 안 되기 때문
  3. 행동 편향을 가지고 출시하라. 나쁜 페이지는 편집 가능하지만 빈 페이지는 불가능

    • 완벽 추구는 마비를 초래하며, 한 번도 만들어보지 않은 것의 이상적 아키텍처를 몇 주간 논쟁하는 엔지니어들을 목격함
    • 완벽한 솔루션은 사고만으로 나오지 않고 현실과의 접촉에서 등장하며, AI가 여러 방식으로 도움을 줄 수 있음
    • 먼저 하고, 제대로 하고, 더 잘하는 순서로 진행 - 못생긴 프로토타입을 사용자 앞에 두고, 지저분한 디자인 문서 초안을 작성하고, 약간 부끄러운 MVP를 출시함
    • 한 주의 실제 피드백에서 한 달의 이론적 논쟁보다 더 많이 학습하며, 추진력이 명확성을 만들고 분석 마비는 아무것도 만들지 못함
  4. 명확성이 시니어의 징표이며, 영리함은 오버헤드

    • 영리한 코드 작성 본능은 엔지니어에게 거의 보편적이며 역량 증명처럼 느껴짐
    • 소프트웨어 엔지니어링은 시간과 다른 프로그래머를 추가할 때 발생하며, 그 환경에서 명확성은 스타일 선호가 아닌 운영 리스크 감소를 의미함
    • 코드는 장애 중 새벽 2시에 유지보수할 낯선 사람들을 위한 전략 메모이므로, 우아함이 아닌 그들의 이해도를 최적화해야 함
    • 가장 존경받는 선임 엔지니어들은 영리함을 명확성과 매번 교환하는 법을 학습함
  5. 새로움은 장애, 채용, 인지 오버헤드로 갚는 빚

    • 기술 선택을 작은 "혁신 토큰" 예산을 가진 조직처럼 다루고, 실질적으로 비표준적인 것을 채택할 때마다 하나씩 소비함 - 많이 감당할 수 없음
    • 요점은 "절대 혁신하지 말라"가 아니라 "고유하게 혁신하도록 보상받는 곳에서만 혁신" 하는 것이며, 나머지는 지루함이 기본값이어야 함
    • 지루함은 알려진 실패 모드를 가지고 있기 때문
    • "작업에 최고의 도구"는 종종 "많은 작업에 걸쳐 최소-최악의 도구" 를 의미함 - 동물원 운영이 실제 세금이 되기 때문
  6. 코드는 당신을 옹호하지 않으며, 사람들이 옹호함

    • 초기 경력에서 훌륭한 업무가 스스로 말할 것이라 믿었으나 이는 오류였으며, 코드는 저장소에 조용히 앉아있을 뿐
    • 관리자가 회의에서 당신을 언급하거나 하지 않고, 동료가 프로젝트에 당신을 추천하거나 다른 사람을 추천함
    • 대규모 조직에서는 초대받지 않은 회의에서, 직접 작성하지 않은 요약으로, 5분과 12개 우선순위를 가진 사람들이 결정을 내림
    • 당신이 없는 방에서 아무도 당신의 영향을 표현할 수 없다면 그 영향은 사실상 선택 사항이며, 이는 자기 홍보가 아니라 가치 사슬을 자신을 포함한 모두에게 읽기 가능하게 만드는 것
  7. 최고의 코드는 작성할 필요가 없었던 코드

    • 엔지니어링 문화에서 창조를 축하하지만, 삭제가 추가보다 시스템을 더 개선하는 경우가 많은데도 코드 삭제로 승진하는 사람은 없음
    • 작성하지 않은 모든 코드 라인은 디버깅, 유지보수, 설명할 필요가 없는 라인
    • 구축 전 질문을 고갈시켜야 함: "그냥... 하지 않으면 어떻게 될까?" - 때때로 답이 "나쁜 일 없음"이면 그것이 솔루션
    • 문제는 엔지니어가 코드를 작성하지 못하거나 AI로 작성하지 못하는 것이 아니라, 너무 잘 작성해서 작성해야 하는지를 묻는 것을 잊는 것
  8. 규모에서는 버그조차 사용자를 보유함

    • 충분한 사용자가 있으면 모든 관찰 가능한 동작이 의존성이 되며, 약속과 무관함 - 누군가는 API를 스크래핑하고, 특이점을 자동화하며, 버그를 캐싱함
    • 이는 경력 수준의 통찰을 창출함: 호환성 작업을 "유지보수"로, 새 기능을 "실제 작업"으로 취급할 수 없으며, 호환성이 곧 제품
    • 시간, 도구, 공감으로 지원 중단을 마이그레이션으로 설계해야 함
    • 대부분의 "API 설계"는 실제로 "API 은퇴" 를 의미함
  9. 대부분의 "느린" 팀은 실제로 정렬되지 않은 팀

    • 프로젝트가 지연될 때 본능은 실행을 비난하는 것 - 사람들이 충분히 열심히 일하지 않음, 기술이 잘못됨, 엔지니어가 충분하지 않음 - 하지만 보통 이것이 실제 문제가 아님
    • 대기업에서 팀은 동시성의 단위이지만, 팀이 곱해질수록 조정 비용은 기하급수적으로 증가함
    • 대부분의 느림은 실제로 정렬 실패를 의미함 - 잘못된 것을 구축하거나, 올바른 것을 호환되지 않는 방식으로 구축함
    • 선임 엔지니어는 "코드를 더 빨리 작성"보다 방향, 인터페이스, 우선순위 명확화에 더 많은 시간을 투자함 - 실제 병목이 거기 있기 때문
  10. 통제 가능한 것에 집중하고, 불가능한 것은 무시하라

    • 대기업에서는 수많은 변수가 통제 밖에 있음 - 조직 변경, 관리 결정, 시장 변화, 제품 전환 - 이것에 몰두하면 행위성 없는 불안을 창출함
    • 제정신을 유지하고 효과적인 엔지니어는 영향력 범위에 집중함 - 재조직 발생 여부는 통제할 수 없지만, 업무 품질, 대응 방식, 학습 내용은 통제 가능함
    • 불확실성에 직면하면 문제를 조각으로 나누고 자신에게 가능한 구체적 행동을 식별해야 함
    • 이는 수동적 수용이 아니라 전략적 집중이며, 바꿀 수 없는 것에 쓰는 에너지는 바꿀 수 있는 것에서 훔쳐오는 에너지
  11. 추상화는 복잡성을 제거하지 않으며, 당신이 온콜일 때로 이동시킴

    • 모든 추상화는 밑에 무엇이 있는지 이해할 필요가 없을 것이라는 내기이며, 때때로 이 내기에 이김
    • 하지만 무언가는 항상 누출되며, 누출될 때 자신이 무엇 위에 서 있는지 알아야 함
    • 선임 엔지니어는 스택이 높아질수록 "하위 레벨" 것들을 계속 학습함 - 향수가 아니라, 추상화가 실패하고 새벽 3시에 시스템과 홀로 있는 순간에 대한 존중 때문
    • 스택을 사용하되, 기본 실패 모드에 대한 작동 모델을 유지해야 함
  12. 글쓰기는 명확성을 강제함. 무언가를 더 잘 학습하는 가장 빠른 방법은 가르치려 시도하는 것

    • 글쓰기는 명확성을 강제하며, 개념을 다른 사람에게 설명할 때 - 문서, 발표, 코드 리뷰 코멘트, 심지어 AI와 채팅 - 자신의 이해에서 공백을 발견함
    • 무언가를 다른 사람에게 읽기 가능하게 만드는 행위가 자신에게 더 읽기 가능하게 만듦
    • 이는 외과의사가 되는 법을 가르쳐서 배운다는 의미는 아니지만, 전제는 소프트웨어 엔지니어링 영역에서 대체로 참
    • 이는 지식을 나누는 관대함만이 아니라 이기적인 학습 해크(hack) 이기도 함 - 무언가를 이해한다고 생각하면 간단히 설명을 시도하고, 막히는 곳이 이해가 얕은 곳
    • 가르치는 것은 자신의 정신 모델을 디버깅하는 것
  13. 다른 업무를 가능하게 하는 업무는 귀중하지만 보이지 않음

    • 글루 작업 - 문서화, 온보딩, 팀 간 조정, 프로세스 개선 - 은 필수적이지만, 무의식적으로 하면 기술 궤적을 정체시키고 번아웃을 초래할 수 있음
    • 함정은 이를 "도움됨"으로 하는 것이지, 의도적이고, 경계가 있고, 가시적인 영향으로 다루지 않는 것
    • 시간 제한을 두고, 순환시키고, 산출물로 전환해야 함: 문서, 템플릿, 자동화 - 그리고 이를 성격 특성이 아닌 영향으로 읽기 가능하게 만들어야 함
    • 귀중하지만 보이지 않는 것은 경력에 위험한 조합
  14. 모든 논쟁에서 이기면, 아마도 조용한 저항을 축적하고 있는 것

    • 자신의 확신을 의심하는 법을 배웠으며, 너무 쉽게 "이길" 때는 보통 무언가 잘못됨
    • 사람들이 당신과 싸우는 것을 멈추는 이유는 당신이 설득했기 때문이 아니라 포기했기 때문이며, 회의가 아닌 실행에서 그 불일치를 표현함
    • 진정한 정렬은 더 오래 걸림 - 다른 관점을 실제로 이해하고, 피드백을 통합하고, 때로는 공개적으로 마음을 바꿔야 함
    • 옳다는 단기적 느낌은 기꺼이 협력하는 동료들과 함께 무언가를 구축하는 장기적 현실보다 훨씬 가치가 적음
  15. 측정이 목표가 되면, 측정을 멈춤

    • 관리에 노출하는 모든 지표는 결국 게임화되며, 악의가 아니라 인간이 측정되는 것을 최적화하기 때문
    • 코드 라인을 추적하면 더 많은 라인을 얻고, 속도를 추적하면 부풀려진 추정치를 얻음
    • 선임의 움직임: 모든 지표 요청에 쌍으로 응답함 - 하나는 속도용, 하나는 품질 또는 리스크용 - 그런 다음 임계값 숭배가 아닌 추세 해석을 주장함
    • 목표는 통찰이지 감시가 아님
  16. 모르는 것을 인정하는 것이 아는 척하는 것보다 더 많은 안전을 창출함

    • "모르겠습니다"라고 말하는 선임 엔지니어는 약함을 보이는 것이 아니라 허가를 창출함
    • 리더가 불확실성을 인정하면 다른 사람들도 동일하게 할 수 있다는 신호이며, 대안은 모두가 이해하는 척하고 문제가 폭발할 때까지 숨겨지는 문화
    • 가장 선임인 사람이 혼란을 인정하지 않는 팀과 그 피해를 목격함 - 질문이 나오지 않고, 가정이 도전받지 않으며, 주니어 엔지니어는 다른 모든 사람이 이해한다고 가정하기 때문에 침묵함
    • 호기심을 모델링하면, 실제로 학습하는 팀을 얻음
  17. 네트워크는 당신이 가질 모든 직장보다 오래 지속됨

    • 초기 경력에서 업무에 집중하고 네트워킹을 소홀히 했으며, 돌이켜보면 이는 실수였음
    • 관계에 투자한 동료들 - 회사 안팎 - 은 수십 년간 혜택을 거둠
    • 그들은 기회를 먼저 듣고, 더 빠르게 다리를 구축할 수 있었으며, 역할에 추천받고, 수년간 신뢰를 쌓은 사람들과 벤처를 공동 창업함
    • 직장은 영원하지 않지만, 네트워크는 각 직장보다 오래 감 - 거래적 허슬이 아닌 호기심과 관대함으로 접근해야 함
    • 떠날 때가 오면, 종종 관계가 문을 여는 것
  18. 대부분의 성능 향상은 영리함 추가가 아닌 작업 제거에서 나옴

    • 시스템이 느려질 때 본능은 추가하는 것 - 캐싱 레이어, 병렬 처리, 더 똑똑한 알고리듬 - 때때로 이것이 맞지만, "계산하지 않아도 되는 것이 무엇인가?"를 묻는 것에서 더 많은 성능 향상을 목격함
    • 불필요한 작업 삭제는 거의 항상 필요한 작업을 더 빠르게 하는 것보다 더 영향력이 있으며, 가장 빠른 코드는 실행되지 않는 코드
    • 최적화 전에 작업이 존재해야 하는지 의문을 가져야 함
  19. 프로세스는 불확실성을 줄이기 위해 존재하며, 서류 흔적 생성을 위해서가 아님

    • 최고의 프로세스는 조정을 더 쉽게 하고 실패를 더 저렴하게 만들며, 최악의 프로세스는 관료적 연극 - 도움이 아니라 잘못될 때 비난을 할당하기 위해 존재함
    • 프로세스가 어떻게 리스크를 줄이거나 명확성을 높이는지 설명할 수 없다면, 아마도 그냥 오버헤드
    • 사람들이 업무를 하는 것보다 업무 문서화에 더 많은 시간을 쓰고 있다면, 무언가 심각하게 잘못됨
  20. 결국 시간은 돈보다 가치가 있게 되며, 그에 따라 행동하라

    • 초기 경력에서는 시간을 돈으로 교환하며, 이는 괜찮지만, 어느 시점에서 계산이 역전됨 - 시간이 재생 불가능한 자원임을 깨닫기 시작함
    • 다음 승진 레벨을 쫓고, 보상의 몇 퍼센트 포인트를 더 최적화하며 번아웃되는 선임 엔지니어들을 목격함
    • 일부는 얻었지만, 대부분은 나중에 포기한 것이 가치가 있었는지 의문을 가짐
    • 답은 "열심히 일하지 말라"가 아니라 "무엇을 교환하고 있는지 알고, 의도적으로 교환하라"
  21. 지름길은 없지만, 복리는 있음

    • 전문성은 의도적 연습에서 나옴 - 현재 기술을 약간 넘어서서 밀어붙이고, 성찰하고, 반복함 - 수년간 - 압축 버전은 없음
    • 희망적인 부분: 학습은 새로운 선택지를 창출할 때 복리로 작용하며, 새로운 사소한 지식만이 아님
    • 글을 쓰되 - 참여가 아니라 명확성을 위해 - 재사용 가능한 원시 요소를 구축하고, 상처 조직을 플레이북으로 수집함
    • 경력을 복리 이자로 취급하는 엔지니어가 복권 티켓으로 취급하는 엔지니어보다 훨씬 앞서 나가는 경향

최종 생각

- 21가지 교훈은 많아 보이지만, 실제로는 몇 가지 핵심 아이디어로 귀결됨: 호기심을 유지하고, 겸손함을 유지하고, 업무는 항상 사람에 관한 것임 - 구축하는 사용자와 함께 구축하는 팀원 - 엔지니어링 경력은 많은 실수를 하고도 앞서 나갈 만큼 충분히 길며, 가장 존경하는 엔지니어들은 모든 것을 올바르게 한 사람이 아니라 잘못된 것에서 배우고, 발견한 것을 공유하고, 계속 나타난 사람들 - 여정 초기라면 시간이 지나면서 더 풍부해진다는 것을 알고, 깊이 있다면 이 중 일부가 공감되기를 희망함


- 조직 내 정보 필터링 구조로 인해 경영진은 엔지니어들의 불만을 퇴사 통보 후에야 알게 되며, 이미 수개월 전에 결정된 퇴사를 되돌리기엔 너무 늦음 - 엔지니어들이 퇴사하는 진짜 이유는 연봉이 아니라 기술적 판단이 무시되는 경험, 갚을 수 없는 기술 부채, 의미 없는 업무에 배치되는 것 - 중간 관리자들은 문제를 자신의 단계에서 해결하려 하며, 이를 전문성으로 인식하지만 실제로는 정보 억압으로 기능함 - 스킵 레벨 대화(경영진이 하위 직급 엔지니어와 직접 대화)는 조직 건강을 해친다는 명목으로 기피되지만, 실제로는 중간 관리자를 책임에서 보호하는 역할임 - 시니어 엔지니어 한 명 교체 비용은 $275,000~$395,000에 달하며, 주 몇 시간의 스킵 레벨 대화 비용($50,000/년)보다 훨씬 큼 - 조직이 초기에 스킵레벨, 외부 진단, 실제 문제 해결에 투자하면 수억 원대 교체 비용을 줄일 수 있고, 그러지 못한 회사는 계속 이유를 모른 채 좋은 엔지니어를 잃게 됨

서론: 한 시니어 엔지니어의 이탈과 1.4M 달러 손실

- 연 4,000만 달러 ARR SaaS 회사에서, 시니어 엔지니어가 확장 불가능한 DB 아키텍처를 6개월간 반대했으나, 제품 조직의 “빨리 출시하고, 나중에 리팩터링 하자”는 결정이 그대로 진행됨 - 엔지니어링 리더십은 그가 옳다고 보면서도 제품 팀에 맞서 밀어붙이지 않았음 - 그는 기술 결정 자체가 아니라, 자신의 판단이 의미 없다는 사실 때문에 그 주에 바로 면접을 보기 시작했음 - 8개월 뒤 시스템은 매일 성능 문제를 겪었고, 18개월 뒤에는 시니어 5명이 회사를 떠났으며, 회사는 왜 이런 일이 벌어지는지 파악하기 위해 fractional CTO를 고용함 - 진단 결과, 임원진은 엔지니어들이 불행하다는 사실을 퇴사 이메일이 오기 전까지 전혀 모르고 있었음 - 퇴사 인터뷰에는 “더 나은 기회”, “더 경쟁력 있는 보상”만 표면적으로 기록됨 - CEO는 남은 엔지니어 연봉을 15% 인상했지만, 그 후에도 이탈은 계속 이어졌음 - 시니어 5명을 교체하는 데 들어간 비용은 채용, 생산성 손실, 지식 유실까지 합쳐 약 140만 달러 수준으로 추산됨 - 이 문제는 보상이 아니라 정보 흐름의 실패에서 비롯되었고, “위로 올라가는 정보가 걸러지는 계층 구조” 가 핵심 원인임

Hierarchies filter bad news out — 계층 구조는 나쁜 소식을 지워버리는 필터

- 조직은 복잡성을 다루기 위해 계층 구조와 관리 레이어를 도입하지만, 그 효과로 정보가 각 레이어에서 걸러지는 구조가 생김 - 주니어가 “문제 있어 보인다”고 말하면, 시니어가 이를 정제해 매니저에게 올리고, 매니저는 “보고할 만한 일인지, 본인에게 불리한지”를 먼저 평가함 - VP→CTO→CEO로 올라갈수록, 원래 문제의 세부 정보와 긴박감이 하나씩 사라짐 - 이 필터링은 악의적이라기보다, 중간관리자가 “자기 레벨에서 문제를 처리하는 것”을 책임이라고 믿는 문화에서 발생함 - “문제를 올리는 것 = 실패”로 간주되고, “해결책만 들고 올라가는 것”이 프로페셔널함으로 인식됨 - 결과적으로, 이는 문제를 숨기는 메커니즘으로 작동함 - 한 120명 규모 엔지니어 조직의 사례에서는, 프론트엔드 팀이 3월에 새 대시보드 성능 문제를 발견해 코드 리뷰에서 공개적으로 우려를 표했음 - 5월에 매니저가 해결 방안을 검토했고, 6월에 VP에게는 “대시보드 성능 최적화 진행 중” 정도로 전달됨 - 7월에 CTO는 “약간의 성능 작업”이 있다는 수준만 들었고, 8월에서야 최대 고객이 대시보드가 못 쓸 정도라고 공식 항의를 하면서 임원진에게 ‘갑작스러운’ 위기처럼 인지됨 - 실제로는 엔지니어들은 5개월간 문제를 알고 있었고, 자원을 배분해야 할 임원진은 항상 수개월 늦은, 희석된 정보로 의사결정을 하고 있었음 - 이 구조에서, 엔지니어는 8월에 DB가 안 버틴다는 걸 알고, 매니저는 10월에 불만을 알고, VP는 12월에 사기 저하를 인지하고, CTO는 2월에 대량 퇴사를 보게 되는 정보 지연 사슬이 반복됨

The convenient fiction of chain of command — ‘보고 라인’이라는 편리한 허구

- 많은 조직이 스킵레벨(임원이 엔지니어와 직접 대화) 을 “체계를 깨는 행동”으로 금기시함 - 중간관리 권위 약화, 불신 신호, 마이크로 매니지먼트라는 이유가 자주 동원됨 - 겉으로는 조직 건강을 위한 고려처럼 보이지만, 실제로는 중간관리층이 책임과 검증에서 자신을 보호하는 장치로 작동함 - CTO가 엔지니어와 직접 이야기하는 경우, 실제로 현장에서 어떤 일이 벌어지는지 알 수 있음 - 반대로, 관리 레이어를 거친 보고서만 보는 CTO는 관리자가 보여주고 싶은 버전의 현실만 보게 됨 - 스킵레벨 금기는 후자를 보장하는 메커니즘이 됨 - “임원 시간은 비싸니, 전략에만 집중해야 한다”는 논리는, 전략이 사실과 다른 정보 위에 세워지기 시작하는 순간 무너짐 - CTO가 매주 몇 시간만 투자해도, 무엇이 실제로 막히고 있는지, 누가 마음이 떠나고 있는지, 어느 기술 베팅이 실패 중인지 직접 들을 수 있음 - 계층을 3–4단계 거친 보고서에 의존하는 편이 시간당 비용은 싸게 보이지만, 잘못된 큰 결정을 반복적으로 낳는 훨씬 비싼 선택이 됨 - 스킵레벨에 쓰는 시간은 많을 필요는 없고, 일관된 패턴과 전체 커버리지만 확보하면 됨 - 조직이 커질수록 한 사람과 대화하는 주기는 길어지지만, CTO가 분기마다 한 번이라도 모든 엔지니어와 직접 이야기하는 구조를 유지할 수 있음 - 이런 직접 대화에서 엔지니어는 매니저에게는 절대 말하지 않을 문제를 이야기하고, 정식 라인보다 3–6개월 빠른 조기 경고가 확보됨 - 한 결제 회사 사례에서, 새로운 CTO가 매주 30분 오피스 아워를 열어 누구나 매니저 허락 없이 예약할 수 있게 했을 때, - 배포 시스템이 불안정해 금요일 배포를 피하는 문화, 거짓 경보가 너무 많아 아무도 믿지 않는 모니터링, API 문서 노후화로 온보딩 한 달이 모두 시행착오로 소비되는 문제가 드러났음 - 각 매니저는 “자기 레벨에서 처리 중”이라며 위로 올리지 않았던 내용이었고, CTO는 분기 안에 세 가지 문제를 모두 해결하도록 예산을 배정했으며, 6개월 뒤 자발적 이직률이 거의 0에 가까워지는 변화가 나타났음

Agency, not salary — 떠나는 이유는 보상이 아니라 ‘에이전시’ 상실

- 엔지니어 이직 사유는 퇴사 인터뷰에서 자주 “보상”으로 포장되지만, 실제 패턴은 더 미묘하고 말하기 난감한 요인에서 출발하는 경우가 많음 1. 첫 번째 패턴은 에이전시 상실임 - 엔지니어가 분명히 실패할 것이라 아는 시스템을 만들도록 요구받고, 비기술적 이유로 판단이 지속적으로 뒤집히는 상태임 - 예측한 실패가 현실이 되었을 때, “왜 막지 않았느냐”는 책임까지 떠안는 구조에서 전문가로서의 판단이 아무 의미 없다는 감각이 누적됨 - 한 핀테크 회사는 이미 검증된 인증 솔루션을 쓰지 않고 직접 구현하기로 했고, 시니어들은 위험과 비핵심 영역이라는 점을 들어 반대했음 - 제품 쪽은 특정 UX 플로우를 위해 커스텀 구현을 강하게 밀었고, 엔지니어링 리더십은 시니어들 의견에 동의했으나 끝내 제동을 걸지 못했음 - 결과적으로 커스텀 인증 시스템은 첫 달에 보안 취약점 3건, 긴급 패치, 6개월 치 개발 리소스를 소모했고, 해당 시니어는 결정이 확정된 주에 바로 이직 준비를 시작했음 - 비용을 숫자로 보면, 커스텀 인증에 18만 달러를 쓰는 동안, 사서 쓸 수 있는 솔루션은 연 1.2만 달러였음 - 그 엔지니어가 남아 있었다면, 로드맵 기준 연 40만 달러 ARR를 창출할 기능에 쓸 수 있었고, - 이 결정으로만 최소 58만 달러가 1년 차에 날아간 셈임 2. 두 번째 패턴은 갚을 수 없는 수준까지 미뤄진 기술적 부채임 - DB 복제, 배포 자동화, 모니터링 개선 같은 핵심 인프라 작업이 매 분기 기능 개발 뒤로 밀리며, - 엔지니어는 언제 어디서, 무엇이 깨질지를 거의 정확히 알고 있음에도 계속 막히는 것을 지켜보는 상황임 - 한 이커머스 회사는 18개월 동안 DB 인프라 작업을 계속 연기했고, 그 사이 트래픽은 연 40%씩 늘어났으나 인프라는 그대로였음 - 19개월 차에 DB가 전체 플랫폼의 병목이 되어, 피크 타임 응답 시간이 200ms에서 4초까지 늘어났고, 전환율 하락으로 120만 달러 수준의 매출 손실과 24만 달러의 긴급 인프라 비용이 발생함 - 인프라 작업을 가장 강하게 요청했던 시니어 3명 중 2명은 이미 떠난 상태였음 3. 세 번째 패턴은 똑똑한 사람에게 멍청한 일을 시키는 구조임 - 연 18만~19만 달러를 받는 시니어에게, 이미 폐기해야 할 시스템 유지보수, 의미 없는 반복 작업, 아무도 믿지 않고 아무도 읽지 않는 문서와 추정 회의를 맡기는 상황임 - 한 SaaS 회사에서는 6년 된 레거시 리포팅 시스템이 1년에 8만 달러 매출을 내고 있었는데, 시니어 한 명(연 19만 달러)이 시간의 60%를 이 시스템 유지에 쓰고 있었음 - 회사는 사실상 연 11.4만 달러를 8만 달러 매출을 위해 태우는 구조였고, 엔지니어는 고객 12명을 신규 플랫폼으로 옮기자고 제안했지만 “전략적 고객”이라는 이유로 거절당했음 - 그녀는 3개월 뒤 퇴사했고, 교체 비용은 리크루팅과 온보딩까지 22만 달러에 달했으며, 고객 마이그레이션은 4만 달러 정도면 끝낼 수 있었던 작업이었음

The early signals executives miss — 임원이 보지 못하는 ‘조기 경고’

- 경영진이 사표를 받기 전까지 못 보는 신호들은 6–12개월 전부터 이미 다른 레벨에서 뚜렷하게 나타남 1. 주니어/미들 엔지니어가 먼저 보는 신호는 시니어의 싸움 포기임 - 시니어가 아키텍처 리뷰에서 더 이상 강하게 의견을 내지 않고, 리뷰는 형식적인 통과 절차가 됨 - 백로그의 기술 작업은 계속 쌓이는데, 배정은 거의 이루어지지 않고, “이건 해봤는데 위에서 안 된다더라”는 말이 빈번해짐 - 한 물류 회사의 주니어 엔지니어는 6개월 동안 이런 변화를 관찰했음 - 과거에는 상세한 코드 리뷰를 하던 아키텍트가 이제 대부분 “LGTM” 한 줄로 승인하기 시작했고, - 비공식 대화에서 “어차피 내 의견은 결과를 못 바꾸니 그냥 시간을 아끼기로 했다”는 말을 들었을 때, 이 시니어가 곧 떠날 거라는 사실을 먼저 깨달았음 - 3개월 뒤 실제로 퇴사가 이뤄졌고, 매니저는 전혀 예상하지 못했다고 느꼈음 2. 시니어 본인이 경험하는 단계는 4–8개월 전부터 나타나는 패턴 인식과 도덕적 피로감임 - “이건 실패할 거고, 리더십은 듣지 않을 거다”라는 확신, 옳지 않은 것을 만들게 되는 도덕적 상처, “내 전문성이 이곳에서는 아무 의미 없다”는 감정임 - 외형상으로는 회의에서 덜 싸우고, 더 협조적으로 보이기 때문에, 리더의 눈에는 오히려 “성숙해진 것처럼” 보일 수 있음 3. 매니저가 보는 신호는 2–4개월 전의 미묘한 행동 변화임 - 1:1에서 참여도가 떨어지고, 팀 토론에서 발언이 줄고, - LinkedIn을 업데이트하고, 업계 행사에 더 자주 나가고, 문서화가 갑자기 매우 꼼꼼해지고, 주니어에게 자신의 시스템을 열심히 가르치기 시작함 - 이때 많은 매니저는 높은 이직률이 본인에게 불리하다고 느껴, 상부 보고 대신 조용히 해결해보려다 이미 오퍼를 받은 뒤에야 사태를 인정하게 됨 - 한 미디어 회사의 매니저는, 어떤 시니어가 갑자기 모든 것을 문서화하기 시작한 것을 “드디어 지식 공유를 실천한다”고 긍정적으로만 봤지만, 실제론 떠나면서 지식 공백에 대한 죄책감 때문에 남기는 기록이었고, 이를 깨달은 건 퇴사 이후였음 4. 임원이 보는 것은 0–1개월 전에야 도착하는 퇴사 통보뿐이고, 그 전 단계에 쌓인 신호들은 계층 구조 속에서 모두 흩어졌던 셈임

Why one departure becomes five — 한 명의 이탈이 다섯 명의 이탈로 번지는 구조

- 한 명의 퇴사는 하나의 데이터 포인트지만, 세 명이 연달아 나가면, 남은 사람들은 “뭔가 있다”고 해석하기 시작함 - 특히 존경받던 동료가 떠나면, 남은 사람은 “저 사람이 보는 뭔가를 나는 못 보고 있는 건 아닐까”를 생각하게 됨 - 외부 리크루터도 이 패턴을 빠르게 감지함 - 같은 팀 출신 엔지니어들이 LinkedIn 검색에서 자주 보이고, 프로필 업데이트가 몰리면, 해당 팀을 집중 공략 대상으로 삼게 됨 - 이직 생각이 없던 엔지니어들도 연락을 받으며 면접을 보게 되고, 그 중 일부는 더 나은 기회를 실제로 발견함 - 한 클라우드 인프라 회사는 시니어 두 명이 3주 간격으로 떠난 뒤, 다음 3개월 동안 추가로 다섯 명을 더 잃었음 - 내부 회고 결과, 첫 두 명의 이탈이 “회사 재무 상태에 이상이 있는 것 아니냐”는 정보 공백에서 오는 불안을 증폭시켰고, - 실제 이유는 앞서 이야기한 에이전시 상실과 기술 부채 문제였지만, 남은 이들이 “내가 모르는 뭔가를 떠난 사람들이 안다”고 추측하는 사이 리크루터가 이 틈을 적극 활용했음 - 지식 유실 비용은 숫자로 잡기 어렵지만, 이후 분기마다 나오는 실수와 지연, 재작업으로 드러남 - 떠난 시니어는 “어떤 3줄의 코드는 건들면 안 되고, 어떤 3,000줄은 없애도 된다”는 감각을 갖고 있었고, - 어떤 고객이 왜 특수한 요구사항을 갖는지, 어떤 기술 부채가 “보기만 나쁜지” vs “실제로 위험한지”를 알고 있었음 - 한 결제 회사 사례에서는, 정산 시스템을 만든 시니어가 퇴사한 뒤, 새 엔지니어가 코드를 수정하면서 어느 결제사 API의 undocumented한 특이 케이스를 몰라 특정 거래 유형이 모두 실패하는 문제가 발생했음 - 이 버그를 찾는 데 2주가 걸렸고, 엔지니어링 비용 약 4.5만 달러, 실패한 거래를 수동으로 맞추는 동안 18만 달러 규모의 문제가 발생했으며, - 원래 시니어는 이 API의 버그를 시행착오로 발견해 머릿속에만 기억하고 있었음

Sometimes it really is the money — 진짜로 보상인 경우와 ‘핑계로서의 보상’ 구분

- 보상이 실제 원인인 경우도 존재하며, 타이밍 패턴이 다름 - 시장 대비 명확히 낮은 연봉, 늘어난 책임에 맞지 않는 보상, 신규 입사자가 본인과 같은 연봉을 받는 구조가 계속될 때, - 이직 전 직접적으로 보상 문제를 먼저 꺼내는 경우는 보상 이슈로 해결 가능한 영역임 - 반대로, 퇴사 인터뷰에서만 “보상”이 등장하고, 이미 몇 달 전부터 조용히 면접을 보고 있던 경우, 보상은 동기가 아니라 정당화 수단인 경우가 많음 - 새로운 오퍼가 30% 높아서 “시장 수준”을 말하지만, 매칭 제안을 해도 “이미 다른 곳을 수락했다”라며 미적지근하게 반응하는 패턴임 - 이런 경우 실제 떠나는 이유는 앞서 말한 에이전시, 기술 부채, 무의미한 업무에 대한 결론임 - 이 글에서 제안하는 간단한 테스트는 “20% 인상으로 붙잡을 수 있는가” 임 - “그 정도면 고민해 보겠다”라면 보상이 주된 이슈일 가능성이 크고, - 그럼에도 이미 마음이 떠난 상태라면, 환경이 망가졌다고 판단한 상태라서 돈만으로는 해결이 안 되는 상황임 - 한 개발자 도구 회사는 보상을 이유로 떠난 8명을 자세히 분석했고, - 이 중 꼭 붙잡고 싶은 5명에게 매칭 이상의 카운터 오퍼를 했지만 3명이 거절했음 - 이후 대화를 통해, 그들이 떠난 이유가 계속 바뀌는 로드맵과 의미 없어진 작업이었고, “회사 방향을 더 이상 믿을 수 없다”는 감각이었음 - 퇴사 인터뷰에서는 “보상”만 언급했지만, 실제 문제는 일 자체의 의미 상실이었음

Prevention beats retention bonuses — 사후 보너스보다 ‘조기 개입’이 훨씬 싸다

- 조기 개입을 위해서는, 계층 필터를 우회하는 정보 채널이 필요하며, 몇 가지 방법이 반복해서 효과를 보이는 패턴임 - 첫 번째는 정기적인 스킵레벨 대화임 - 매주 몇 시간만 엔지니어 전 레벨과 직접 대화에 쓰면, 정식 라인에서는 올라오지 않는 실제 생산성 장애물, 문제되는 기술 결정, 떨어지는 사기 지점을 들을 수 있음 - 조직이 커지면 한 사람과의 빈도는 줄이되, 전체 커버리지를 유지하는 설계가 중요함 - 한 100명 규모 엔지니어 조직의 CTO는 매주 4시간을 스킵레벨에 쓰며, - 30분씩 2주 회전으로 한 달 약 30명, 7개월이면 조직 전체를 한 바퀴 도는 구조를 만들었음 - 일정이 항상 열려 있어 엔지니어가 필요할 때 예약할 수 있었고, 그 결과 자발적 이직률이 연 18%에서 7%로 감소했으며, CTO는 이를 “문제가 작고 고칠 수 있을 때 듣게 된 덕분”이라고 인식함 - 두 번째는 외부 진단 관점임 - 리포트라인에 속하지 않고, 인사 권한이 없는 fractional CTO나 외부 어드바이저가 인터뷰를 하면, 사람들은 훨씬 솔직하게 말하는 경향이 있음 - 내부 임원도 같은 질문을 할 수 있지만, 답이 달라지는 이유는 ‘답변자가 느끼는 위험’ 차이임 - 한 SaaS 회사는 6개월 동안 시니어 4명을 잃은 뒤 fractional CTO를 투입해 전 레벨 인터뷰를 진행했고, - 엔지니어들이 느끼는 핵심 문제는 “아키텍처 리뷰가 실제 결정을 하는 장이 아니라, 나중에 책임을 돌리기 위한 연극처럼 느껴진다”는 점이었음 - 리뷰 요구사항을 다 반영해 통과해도, 몇 주 뒤 제품 결정으로 다시 뒤집히는 일이 반복되었고, - 회사는 이 프로세스를 없애고, 기술 구현에 대해 엔지니어링에 실질적인 거부권을 부여했음 - 세 번째는 적어도 하나는 실제로 고치는 것임 - 이야기만 듣고 아무 변화가 없으면 “말해도 소용없다”는 학습이 이루어지고, - 한 가지라도 눈에 보이게 고치면 “말하면 바뀐다”는 신호가 조직 전체에 퍼짐 - 한 헬스케어 기술 회사에서, 개발자가 CTO와 스킵레벨에서 테스트가 45분 걸려 개발 속도가 느리다고 말했을 때, - CTO는 “왜 아직도 안 고쳤는지”를 물었고, 개발자는 여러 차례 매니저에게 이야기했지만 매번 후순위가 됐다고 답했음 - CTO는 즉시 2주 작업 시간을 배정했고, 테스트 시간을 8분으로 줄이는 데 성공했으며, - 이 경험이 입소문을 타며, 다른 엔지니어들도 스킵레벨에서 문제를 가져오기 시작했음 - 비용은 약 1.2만 달러의 엔지니어 시간이었지만, “말하면 바뀐다”는 신호 가치가 훨씬 컸음

The $1.4m misdiagnosis — 잘못된 진단이 낳는 140만 달러짜리 오판

- 시니어 한 명을 교체하는 데 드는 보수적인 비용 추산은 27.5만~39.5만 달러 수준임 - 리크루팅 비용: 연봉의 20–25% 수준 - 공석 기간 생산성 손실: 3–6개월 동안 연 15만 달러 생산성 기준 4만~7.5만 달러 - 온보딩 기간 반쪽짜리 생산성: 6개월 동안 50% 효율로 일하는 손실분 3.5만~4만 달러 - 여기에 정량화하기 어려운 지식 손실, 의사결정 지연, 재작업이 뒤따름 - 시니어 5명을 잃으면 1.4~2.0M 달러 규모의 손실이 발생하는 셈이며, 앞서 사례 회사의 비용 추산과 맞물림 - 반대로, 실제 문제를 고치는 비용은 훨씬 작지만, 조직 구조와 정치에 손을 대야 한다는 부담이 있음 - 기능 몇 개를 미루고, 불필요한 프로세스를 제거하고, 기술 결정을 엔지니어에게 넘기는 대신, - 많은 조직은 “엔지니어는 원래 비싸고, 시장 경쟁이 치열하다”는 설명을 선호함 - 이 설명은 내부 변화를 요구하지 않기 때문에 편하지만, 실제 원인(정보 구조, 결정 방식)을 가리는 역할을 함 - 한 Series B 회사는 18개월 동안 엔지니어 7명을 잃으면서, CEO는 “경쟁사가 더 많이 준다”고 생각했지만, - 사후 분석 결과, 이들 중 5명은 떠나기 6개월 전 구체적인 기술 결정, 프로세스 부담, 우선순위 문제를 매니저에게 이야기했었음 - 이 이슈들은 1:1 메모에 “모니터링 중” 정도로만 기록되고, 어느 레벨에서도 임원에게 올라가지 않았음 - 그 결과, 임원은 “보상 문제”라고 믿었고, 엔지니어들은 “내 의견은 중요하지 않다”고 결론 내린, 서로 완전히 다른 현실을 살고 있었음

Information is cheaper than replacement — 정보 확보는 교체보다 압도적으로 싸다

- 떠나는 엔지니어들은 임원들이 모르는 사실을 알고 있음 - 어떤 기술 결정이 실패하고 있는지, 어느 프로세스가 시간을 낭비하는지, 어떤 관리 방식이 사람을 지치게 하는지에 대한 구체적인 정보임 - 이 정보는 조직 안에 항상 존재하지만, 계층 필터와 중간관리 인센티브 때문에 임원 레벨까지 올라오지 못함 - 핵심 질문은 “퇴사 전에 이 정보를 들을 것인가, 아니면 그들이 막았을 문제를 사고로 겪고 나서야 배울 것인가”임 - 이를 잘 하는 조직은 정보 흐름을 전략적 자산으로 보고, 스킵레벨 채널을 공식적으로 설계하고, - 필터링된 보고서보다 현장으로부터 직접 얻는 ‘그라운드 트루스’에 더 높은 가치를 두는 구조를 갖춤 - 이런 조직에서는, 임원 시간이 잘못된 전략 회의에 쓰이는 것보다, 매주 몇 시간이라도 현장 이야기를 듣는 데 쓰일 때 ROI가 더 크다는 사실을 인정함 시니어 한 명의 이직만 막아도, 연간 수만~수십만 달러 수준의 임원 시간 투자를 다섯 배 이상 회수하는 계산이 나옴 - 반대로, 이걸 잘못 다루는 조직은 관리자의 편안함을 정보 정확도보다 우선시함 - 스킵레벨을 금기시하고, 나쁜 소식이 제거된 보고서에 의존하며, - 이직 문제를 사표에서야 인지하고, 수백만 달러를 들여 사람을 교체하면서도 “왜 최고의 사람들이 떠나는지”를 끝내 이해하지 못함 - 맨 처음 사례의 시니어 엔지니어는 지금 기술 아키텍처에 대해 엔지니어링에 거부권이 있고, CTO가 정기적으로 스킵레벨을 유지하는 회사에서 일하고 있음 - 그도, 동료들도 이직 준비를 하지 않고 있고, 회사의 자발적 이직률은 같은 단계 회사 평균의 절반 이하인 12% 수준임 - 이 회사 임원들은 “무너질 수 없을 만큼 늦기 전에” 무엇이 망가졌는지 듣고 있고, 이는 단순한 메커니즘, 즉 꾸준히 묻고, 말할 수 있는 구조를 유지하는 것으로 만들어진 결과임

- 시니어 엔지니어 이탈 문제는 정보 흐름이 아닌 경영진 인센티브 구조의 문제이며, 분기별 성과에 최적화된 보상 체계가 장기 투자를 요하는 인재 유지와 근본적으로 충돌 - 시니어 엔지니어 1명 이탈 시 총비용은 50만~100만 달러에 달하며, 채용비, 공석 비용, 온보딩, 부족 지식(tribal knowledge) 손실 등이 여러 예산에 분산되어 보이지 않음 - 14개월 전 경고를 무시한 결제 처리 회사가 블랙프라이데이에 347만 달러 손실을 입은 사례에서, 원래 수정 비용은 8만 달러에 불과했음 - 6가지 구조적 개입(이탈비용 회계, 사고 추적, 경영진 온콜, 보상에 유지율 반영, 기술자문위원회, IC 트랙 동등 보상)이 인센티브를 재정렬하는 해결책으로 제시 - 이러한 개입은 경영진이 유지율을 경제적 문제로 인식하고 구조적 변화를 수용할 의지가 있을 때만 효과적이며, 형식적 시행은 오히려 역효과 초래

정보가 흘러도 행동이 바뀌지 않는 이유

"The constraint is not information flow. It is economics."

- 이 글은 엔지니어 이탈을 다룬 연작의 2편으로, 1편 왜 당신의 최고 엔지니어들이 다른 곳에 면접을 보고 있을까에서 다룬 정보 비대칭 문제 이후의 상황을 이어서 다룸 - 1편에서는 시니어 엔지니어가 왜 떠나는지 설명했으며, 핵심 원인은 문제가 존재해도 경영진에게 도달하지 않는 구조였음 - 하지만 이 글은 그 가정을 한 단계 더 밀어붙임 - 정보 흐름을 실제로 개선했을 때 어떤 일이 벌어지는 지를 다룸 - 결론은 직관과 다르게, 아무것도 바뀌지 않는 경우가 대부분이라는 점임 - 조직은 문제를 인지하기 위해 여러 장치를 도입함 - 스킵 레벨 1:1 미팅 도입 - 익명 피드백 채널 운영 - 외부 컨설턴트를 통한 리텐션 설문 진행 - 그 결과 엔지니어들은 매우 명확하게 문제를 전달함 - 기술 부채가 사기를 갉아먹고 있음 - 아키텍처 결정에서 전문성이 무시되고 있음 - 온콜 부담이 지속 가능하지 않은 수준임 - 경영진은 이를 듣고 고개를 끄덕임 - 문제를 인정함 - 우선순위를 조정하겠다고 말함 - 그러나 분기가 바뀌면 의사결정은 이전과 동일하게 반복됨 - 동일한 방식으로 분기 목표를 달성함 - 그리고 그 방식은 방금 들은 문제들을 다시 무시하는 것임 - 이 지점에서 글은 핵심을 분명히 함 - 문제는 정보 부족이 아님 - 문제는 경제적 구조, 즉 인센티브 설계임

핵심 문제: 임원의 인센티브 구조

- VP of Engineering이 10월에 직면한 의사결정 계산 사례 - 분기 성과 검토 3개월 전이고 엔지니어의 주식 베스팅까지는 6개월 남음 - 한 명의 시니어 플랫폼 엔지니어가 다음과 같이 요청함 - 인증 시스템을 6주간 리팩터링하고 싶다는 것 - 기술 부채가 누적되어 있고 구조가 취약해짐 - 보안 연구자 두 명이 이미 위험 신호를 전달했음 - 하지만 현재 상태는 애매함 - 실제 장애는 없고, 고객 불만도 없고, 매출 영향도 없음 - 존재한다는 것은 “지금 고치지 않으면 위기가 된다”는 엔지니어의 경고뿐임 - VP에게는 두 가지 선택지가 존재함 - 선택지 A: 리팩터링 승인 - 6주간 기능 개발 속도 감소 감수 - 분기 OKR 미달 가능성 발생 - CEO에게 “고객이 보지 못하는 기술 작업” 때문에 로드맵이 지연된 이유를 설명해야 함 - 이미 세일즈 팀이 약속한 기능 출시 일정이 흔들릴 위험 - 결과적으로 연말 보너스에 직접적 악영향 가능성 존재 - 이 선택의 보상은 12~18개월 뒤에 받음 : 해당 시니어 엔지니어가 “기술 판단이 존중된다” 는 이유로 조직에 남는 것 - 선택지 B: 기능 우선 결정 - 기술 부채를 “중요하다”고 인정하되 “다음 분기”로 미룸 - 예정된 로드맵을 그대로 출시하여 OKR 달성하고, 보너스 수령 - 시니어 엔지니어는 당장은 남아 있음. 스톡 옵션이 아직 베스팅되지 않았기 때문 - 인증 시스템이 나중에 터지면 그것은 미래 분기의 문제 - 엔지니어가 6개월 뒤 떠나면 채용으로 대체 가능하다고 판단 - 이 구조에서 선택지 B가 항상 이김 - 결국 실패할 때까지 - 제품 출시 중 핵심 시스템 장애가 발생하고, 18개월 사이 시니어 엔지니어 5명 이탈하며, CFO가 “왜 재채용 비용으로 매년 140만 달러를 쓰고 있는가”를 묻기 시작할때까지 B가 이김 - 근본적 불일치 이기 때문 - 임원 보상 구조는 분기 단위 성과에 최적화되어 있음 - 하지만, 엔지니어 유지와 기술 부채 해소는 장기 투자를 요구함 - 정보 흐름 개선만으로는 이 간극을 메울 수 없음 - 경제적 구조 자체의 재설계가 해결책

Why the Math Favors Dysfunction - 계산해보면 장애가 나는게 당연함

- 숨겨진 비용은 눈에 보이지 않는 방식으로 작용하여 합리적인 행위자들이 비합리적으로 행동하게 만듦 - 연봉 $200,000 수준의 시니어 엔지니어 1명이 떠날 경우 실제 총비용은 $500,000 ~ $1,000,000 이상으로 계산됨 - 대부분의 경영진은 이 수치를 듣고 과장된 것이라고 생각하지만 그렇지 않음. 계산 방법은 다음과 같음 - 직접 대체 비용: $85,000-$100,000 - 채용 수수료: 외부 리크루터 수수료 20-25% 로 20만 달러 엔지니어의 경우 $40,000-$50,000 - 내부 채용(구인 게시판, 소싱 도구, 리크루터 급여) 자체 처리 시 $15,000-$20,000 - 사이닝 보너스: 경쟁 시장에서는 시니어 후보 확보에 $20,000-$40,000 필요 - 특히 그들의 현재 회사에 지분을 남겨두고 옮길 때는 필수 - 이사 비용: 해당 시 국내 이사라면 $10,000-$30,000, 해외는 더 높음 - 공석(Vacancy) 비용: $50,000-$100,000 - 시니어 엔지니어 채용까지 평균 3-6개월 소요 - 공석 기간 동안 해당 엔지니어의 업무는 중단되지 않고 두 가지 비용이 동시에 발생함 - 하나는 업무 재분배로 인해 팀 생산성이 저하되는 것이고, 다른 하나는 업무 포기로 인해 기회비용이 발생하는 것 - 업무 재분배 비용 $25,000-$40,000: - 떠난 엔지니어 업무의 약 60% 가 남은 팀원에게 분산 - 업무 자원의 자유로운 재배치가 아니라 생산성 저하로 이어짐 - 이미 업무량이 포화 상태인 엔지니어들은 이제 익숙하지 않은 영역의 코드 리뷰를 처리하고, 자신이 개발하지 않은 시스템에 대한 질문에 답하며, 완전히 이해하지 못하는 서비스를 유지 관리해야함 - 엔지니어 3명이 각각 20% 추가 업무 흡수 시 단순히 20% 더 일하는 것이 아니라 컨텍스트 전환으로 전체 효율 감소 - 공석 기간 동안 엔지니어당 10-15% 생산성 손실을 초래 - 업무 재분배 비용 계산 - 업무를 흡수하는 엔지니어 수 × 생산성 감소율 × 공석 기간(개월) × (평균 연봉 / 12) - 일반적인 시나리오에서는 3명의 엔지니어 × 12% 생산성 감소 × 4개월 × ($180,000 / 12) = $21,600 - 이탈한 엔지니어가 인프라·보안·플랫폼처럼 전문성이 높은 영역을 담당했을 경우 $30,000–$40,000 수준까지 상승 - 업무 포기 비용 $25,000-$60,000: - 나머지 40%는 재분배되지 않고 이연 또는 완전 포기 - 플랫폼 개선, 기술 부채 감소, 아키텍처 진화, 문서화, 멘토링 등 기능 출시와 무관하지만 미래 위기 예방 업무인데 조용히 로드맵에서 제외됨 - 업무 포기(Work Abandonment)의 즉각적인 비용은 수행되지 않은 작업의 급여 환산 가치로 계산됨 - 이탈한 엔지니어 업무의 40% 가 공석 기간 동안 수행되지 않음 - 계산식은 40% × 4개월 × ($200,000 / 12) = $26,667 - 하지만 진짜 비용은 즉각적으로 끝나지 않음 - 미뤄진 작업은 이후 분기들에 걸쳐 누적 비용을 만들어냄 - 예를 들어 - 시니어 인프라 엔지니어가 계획했던 데이터베이스 최적화가 연기되면 - 쿼리 성능이 점진적으로 저하되고 - 결국 원래 작업 범위를 훨씬 넘는 긴급 대응이 필요해짐 - 해당 엔지니어가 담당하던 아키텍처 리뷰가 중단되면 - 비용이 큰 실수를 사전에 걸러낼 수 있는 전문성이 사라진 상태에서 - 기술적 의사결정이 진행됨 - 측정 가능한 업무 포기 비용은 - “원래 했어야 했지만 하지 않은 작업”의 가치임 - 보수적인 계산식은 다음과 같음 - (포기된 업무 비율 × 연봉 / 12) × 공석 개월 수 (40% × $200,000 / 12) × 4개월 = $26,667 - 현실적인 업무 포기 비용 범위는 $25,000–$60,000 - 포기된 작업이 예방적인지 기능 중심적인지 비중에 따라 달라짐 - 총 공석 비용(Combined Vacancy Cost): $50,000–$100,000 - 업무 재분배 비용 $25,000–$40,000 + 업무 포기 비용 $25,000–$60,000 두 항목을 합산한 결과임 - 이는 4개월간 자리가 비어 있는 상태에서 발생하는 직접적이고 측정 가능한 영향만을 반영한 수치임 - 계산 자체는 보수적으로 이루어짐 - 온보딩 및 적응 비용: $100,000-$125,000 - 새 시니어 엔지니어 생산성: 1개월차 약 25%, 2-3개월차 50%, 4-5개월차 75%, 6개월차 전체 생산성 도달 - 1개월 차: 생산성 75% 손실 = (200,000달러 / 12개월) × 0.75 = 12,500달러 - 2~3개월 차: 생산성 50% 손실 = (200,000달러 / 12개월) × 0.50 × 2 = 16,667달러 - 4~5개월 차: 생산성 25% 손실 = (200,000달러 / 12개월) × 0.25 × 2 = 8,333달러 - 첫 6개월 생산성 격차 총합: $37,500 - 온보딩 인력 비용: 새 시니어 엔지니어가 첫 달 주당 10-15시간, 2-3개월차 주당 5-8시간의 다른 엔지니어 시간 소비 - 1개월 차: 주 12시간 × 4주 × 시간당 90달러 = 4,320달러 - 2~3개월 차: 주 6시간 × 8주 × 시간당 90달러 = 4,320달러 - 시간당 $90 기준 온보딩 인력 비용: $8,640 - 즉 첫 6개월 동안 $46,140의 손실 이 발생함 - 하지만 대부분의 시니어 엔지니어가 이전 엔지니어 수준의 도메인 전문성에 도달하는 데 약 1년 소요되므로 $92,000-$125,000 예상 - 부족 지식(Tribal Knowledge) 손실: $100,000-$300,000 - 가장 정량화하기 어렵지만 이후 분기에 실수로 나타남 - 이탈 엔지니어가 알고 있던 것들: - 코드베이스의 어느 부분이 취약하고 신중한 변경이 필요한지 - 어떤 고객이 특별 요구사항을 가졌고 그 이유 - 어떤 아키텍처 결정이 의도적 트레이드오프인지 vs 기술 부채인지 - 10,000줄 서비스에서 실제로 중요한 3줄의 코드 - 특정 데이터베이스 쿼리가 비효율적으로 보이지만 그렇게 작성된 이유(3년 전 발견된 특정 조건에서 "명백한" 최적화가 데이터 손상 유발) - 맥락 부재로 인한 실수: 새 엔지니어가 "느린" 쿼리 최적화 시 회사 2대 고객의 핵심 워크플로 중단 - 문제 파악 2일($4,615), 적절한 수정 구현 1주($7,692), 고객 관계 복구 - 단일 사고 비용 약 $12,000-$15,000, 이탈 시니어 엔지니어당 첫해 3-5회 발생 - 의사결정 지연: 이탈 엔지니어가 30초에 답했을 질문이 이제 3시간의 코드 고고학, Slack 히스토리 검색, "왜 이렇게 했는지 아는 사람?" 대화 필요 - 주 2회, 6개월간 발생 시: $14,040 - 이연 또는 포기된 프로젝트: 이탈 엔지니어만이 인증 시스템을 충분히 이해해 SSO 통합을 안전하게 구현 가능 - 해당 프로젝트 6-9개월 지연, SSO가 50만 달러 기업 계약에 필요했다면 지연 비용 측정 가능 - 이런 내부 지식 손실에 대한 보수적인 추정치: 퇴사 후 12개월 동안 10만 달러에서 30만 달러까지 - 엔지니어 이탈당 총비용 - 직접 대체: $85,000-$100,000 - 공석 비용: $50,000-$100,000 - 적응 및 온보딩: $92,000-$125,000 - 내부 지식 손실: $100,000-$300,000 - 보수적 총계: $327,000-$625,000 - 프로젝트 지연 및 기회비용 포함 현실적 총계: $500,000-$1,000,000 - 이러한 비용은 예산 전체에 분산되고 노이즈에 숨겨짐: 채용비는 HR 예산, 생산성 손실은 추적 안 됨, 내부 지식 증발은 분기 보고서에 미표시 - 기술 부채 이연 및 기능 우선 결정은 즉각적이고 가시적인 성과 창출: 영업팀 데모, 마케팅 출시 발표, CEO의 이사회 보고 등 - 경제학자들이 "끓는 개구리" 문제라 부르는 현상임: - 개별 직원의 퇴사는 감당할 수 있는 것처럼 보이고, 기술 업무 연기는 합리적으로 보이며, 분기별 절충안도 개별적으로는 정당해 보임 - 패턴이 명백해질 때(연간 시니어 엔지니어 이탈률 18%, 누적 기술 부채, 연쇄 시스템 장애)에는 조직은 이미 기능 장애를 정상적인 것으로 받아들이게 됨

회복(Recovery)은 어떤 모습으로 보이는가

- 블랙프라이데이 재앙 14개월 전, 중견 결제 처리 회사의 시니어 플랫폼 엔지니어가 구체적 우려를 제기 - "트랜잭션 처리 시스템이 예상 홀리데이 트래픽을 감당하지 못합니다" - 데이터베이스 샤딩 및 큐 최적화가 필요하다는 상세 제안 제출: 엔지니어링 시간 6주, 인프라 비용 $80,000 추정 - VP of Product에 의해 우선순위가 밀림: - 다른 두 개의 기능 출시가 더 중요하다고 판단됨 - 분기 리뷰에서는 "잠재적 문제를 사전에 파악한 능력"을 칭찬했지만, 아키텍처 제안서는 Jira에 방치함 - 해당 엔지니어는 4개월 후 15% 인상 받고 경쟁사로 이직, 3개월 탐색과 $47,000 채용비 소요 후 대체 인력 채용, 생산성 도달에 5개월 추가 소요 - 그 사이 시니어 엔지니어 2명 추가 이탈: 1명은 기술 부채 좌절감, 1명은 사내에 없던 Principal Engineer 역할을 외부에서 수락 - 그 최초 경고가 다시 논의된 시점은 9개월 후 아키텍처 리뷰 - 당시에는 이미 제안의 맥락과 해결 방법에 대한 조직적 기억이 사라진 상태 - 주니어 엔지니어에게 “대안을 조사”하라는 임무가 할당됨 - 블랙프라이데이 당일, 오전 9시 47분 트랜잭션 급증하며 재앙이 시작 - 오전 10시 23분부터 데이터베이스가 쓰기 요청을 거부 - 병목은 14개월 전 지적된 바로 그 지점이며, 해당 장애로 인해 $2.5M 규모의 트랜잭션 처리 실패 - 복구에는 5시간이 소요됨 - 긴급 인프라 확장 비용 $180,000 과 영구적 아키텍처 변경을 위해 엔지니어 3명이 휴일 내내 초과 근무 - 12월 3일, CTO 주도로 새로운 항목이 포함된 포스트모템이 경영진에 제출됨 - “Previously Raised Concerns” 섹션 추가되어 그 엔지니어의 최초 경고, 우선순위 배제 결정, 이후의 인력 이탈을 모두 기록 - CFO가 총비용을 계산해 봄 - 엔지니어 이탈 비용(시니어 3명) : 1인당 $235,000의 측정 가능한 비용 발생 - 리크루팅 $47,000 + 사이닝 보너스 $30,000 + 공석 비용 $83,000 (평균 4개월) + 온보딩·램프업 $75,000 - 총합 $705,000 - 트라이벌 지식 손실 비용: $2.2M - 데이터베이스 구조, 실패 모드, 기존 해결책에 대한 이해가 조직에서 사라짐 - 문제를 다시 발견하고, 해결책을 재조사하고, 긴급 상황에서 구현해야 했음 - 이러한 지식 격차는 계획된 마이그레이션을 위기 대응으로 바꿔 버림 - 조사 비용, 잘못된 시도, 긴급 벤더 투입, 가맹점 대응 비용이 누적됨 - 실패한 트랜잭션 비용: - 결제 처리 실패 금액 $2.5M - 수수료율이 2.9%였으므로 직접 매출 손실은 $72,500 이지만, 계약상 모든 거래를 처리해야할 의무가 있음 - 그래서 처리 실패로 인한 SLA 위반 벌금 $180,000 및 가맹점 지원 및 이탈 방지 비용 $45,000 발생 - 긴급 인프라 비용: $180,000 - 긴급 데이터베이스 확장(추가 읽기 복제본, 업그레이드 된 인스턴스, Expedited 벤더 서포트 비용) - 로드밸런서 재설정 및 CDN 최적화로 14개월 전에 예상했던 트래픽을 처리 가능하게 함 - 복구 및 사후 조치 비용: $87,000 - 시니어 엔지니어 3명 휴일 주말 72시간 2.5배 초과근무율 근무: $51,923 - 더 넓은 엔지니어링 팀 2주 후속 작업: $38,462 - 총 사고 비용: $3.47M - 원래 제안된 예방 비용: $80,000 (선임 엔지니어 1명의 6주 엔지니어링 작업 시간과 인프라 비용 포함) - 포스트모템 첫 페이지에는 $3.47M vs $80,000 라고 적혀있는데 이 숫자가 대화의 방향을 바꿈 - CEO가 이사회 질문에 대응해 유지율 분석을 지시 - 시니어 엔지니어 이탈률 연간 34%(수익성 있는 회사 업계 평균의 2배 이상) - 이전에 경영진 검토 없이 보관된 퇴사 인터뷰에서 일관된 패턴이 나타남 - 재능 있는 엔지니어들이 기술적 우려가 인정되었지만 실행되지 않을 때 이탈 - 18개월간 4가지 개선조치 시행: - CFO가 분기 보고서에 고객 획득 비용과 함께 이탈 비용 추적 시작 — 갑자기 $235,000 평균 이탈 비용이 마케팅 지출 결정같은 문서에 등장 - 모든 경영진 분기별 온콜 로테이션에 참여 — 데이터베이스 작업 우선순위 하향한 VP of Product는 첫 주 23페이지 분량의 보고서를 수신했는데, 19건이 이전 6개월간 지적된 기술 부채 관련 - 보상위원회가 임원 변동 급여에 인재 유지 요소를 추가: 시니어 엔지니어 90% 연간 유지하는 것은 보너스 계산의 25% 가치 - 디렉터 및 VP 수준과 보상 일치하는 Staff 및 Principal IC 트랙 신설 - 18개월 후, 이 결제회사는 시니어 엔지니어 이탈률이 연간 9%로 감소 - 더 중요한 것은 아키텍처 리뷰 프로세스 변화: - 기술 부채 제안에 이제 계산된 장애 비용이 포함 - 경영진은 일상적으로 "이것을 이연하면 엔지니어 이탈 위험은 얼마나 되나요?"라고 질문 - 원래 데이터베이스 우려 제기했던 플랫폼 엔지니어가 Principal Engineer로 복귀 - 퇴사 당시 대비 연봉 40% 인상 — 특별히 인프라 확장 리드로 채용 해당 엔지니어의 복귀는 숫자가 보여준 변화와 함께 조직의 경제적 계산이 실제로 바뀌었음을 상징함

실제로 효과가 있었던 여섯 가지 개입(Intervention)

- 인센티브를 재정렬하는 구조적 개입, 정보 수집이나 공감 활동이 아니라 경제적 재설계 - 영향력의 계층 구조 - 6가지 개입이 부담스러워 보일 수 있으나 구현 난이도와 가치 창출 시간이 동일하지 않음, 순서가 중요 - 가장 빠른 성과를 내는 방법: 조직의 최소한의 동의만 필요한 개입 - 이탈비용 회계(#1): CFO 승인과 재무 분석가 시간만 필요 - 무시된 경고로 인한 사고 추적(#2): SRE 프로세스 변경만 필요. 예산이나 구조 조정이 필요 없고, 체계적인 사후 분석 문서화만 있으면 됨 - 둘 다 30일 내 시작 가능, 더 어려운 싸움을 위한 정량화된 증거 제공 - 중기 개입: 문화 변화 필요하지만 보상 구조조정 불필요 - 경영진 온콜 로테이션(#3): 한 경영진이 인프라 개선 작업 연기의 결과를 직접 경험할 때 성공하며, 그 정책은 자연스럽게 자리 잡음 - 권한 있는 기술자문위원회(#5): 경영진이 자신의 결정이 번복될 가능성을 진정으로 받아들일 때 효과를 발휘하며, 소규모로 시범 운영되는 방식은 몇 분기 내에 실패 - 구현 일정 3-6개월 : 단순한 정책 변경뿐 아니라 신뢰 구축을 필요로 하기 때문 - 구조적 개입: 이사회 또는 보상위원회 승인 필요, 6-12개월 소요하지만 가장 깊은 변화 제공 - 보상 체계 내 인재 유지 지표 포함(#4): 경영진 보너스가 시니어 엔지니어 유지에 의존 시 기술 부채가 하룻밤 사이 전략적으로 중요해짐 - IC 트랙의 동등성 확보(#6): Staff 엔지니어가 팀 관리 없이 수석급 급여 시 기술 전문성 유지가 구조적으로 가능해짐 - 최소 실행 가능 개입: 다른 계층의 두 요소 결합 - 이탈비용 회계(빠른 성과) + 보상에 유지 지표(구조적 변화) - 첫 번째가 비즈니스 케이스 구축, 두 번째가 경영진에게 행동을 합리적으로 만듦 - 위기 상황 회사: 빠른 성과 즉시 시행 + 구조적 변화 병행 설계 - 조기 경고 징후 회사: 측정(비용 회계, 사고 추적)으로 시작 + 결과 데이터로 더 깊은 개입 정당화 1. 이탈 비용 회계 (Cost-of-Attrition Accounting) - 보이지 않는 것을 보이게 만들기: 모든 시니어 엔지니어 이탈의 전체 비용 계산 - 평균 리크루팅 비용 $35,000 - 완전한 생산성 도달까지 약 6개월(시니어 엔지니어 연봉의 50%) - 지식 손실로 인한 프로젝트 지연 - 해당 엔지니어만 이해했던 아키텍처 결정의 기회비용 - 이 수치를 월별로 추적하고 CAC, 매출 지표와 같은 임원 대시보드에 포함시킴 - 한 금융 서비스 회사는 분기별 이탈 비용을 추적한 결과 - Q1: 시니어 2명 이탈, $400,000 - Q3: 연간 예상 비용 $900,000 - CFO가 이를 연간 엔지니어링 예산 $3M과 함께 제시하자 - CEO의 질문이 “왜 떠났나”에서 “막으려면 얼마가 필요한가” 로 바뀜 - 결과적으로 기술 부채 정리와 보상 조정에 $400,000 투자하자 - 시니어 이탈률 43% 감소하고 투자 비용은 두 분기 만에 모두 회수 2. 무시된 경고로 인해 발생한 사건을 추적하기 - 사후분석 템플릿을 수정하여 필수 섹션인 "사전 경고(Prior Warnings)" 추가 - 담당자가 Jira, Slack, 아키텍처 리뷰 노트, 이메일에서 이 장애 모드에 대한 과거 경고를 확인하도록 요구 - 문서화 항목: 경고 제기 시점, 제기자, 제안된 조치, 우선순위 하향 이유 - 사고 비용 계산: 다운타임 수익 영향, 고객 지원 부담, 복구에 소요된 엔지니어 시간 - 한 헬스케어 기술 회사는 이 방식을 도입한 뒤 - 6개월 내 프로덕션 사고의 70%가 사전 예측되었음을 발견 - 엔지니어들이 우려 제기했지만 경영진이 기능 개발에 집중하느라 문제 해결 우선순위 하향 - 1년간 총 비용: 예방 가능한 사고에 180만 달러의 비용이 발생 함 - 16건 주요 장애 중 14건에서 기술 경고가 정확했음을 경영진이 확인 시 패턴의 심각성을 깨달음 - 예측이 일관되게 정확함이 입증되자 행동 변화 3. 경영진 온콜 로테이션 - 모든 임원(프로덕트, VP, 디렉터 포함)이 분기당 1주씩 온콜 수행 - 에스컬레이션 정책: - 온콜 엔지니어가 알림이 이전에 우선순위가 낮았던 수정 사항이나 연기된 기술 작업과 관련이 있다고 판단할 경우 - 시간이나 요일에 관계없이 해당 결정을 내린 책임자에게 직접 보고 - 이는 어떤 대시보드보다 강력한 체험 학습을 제공함 - 사례: 한 VP of Product가 7개월 전 엔지니어들이 "있으면 좋은" 수정으로 플래그한 동일한 데이터베이스 연결 풀 이슈로 5일간 17건의 호출을 경험 - 해당 이슈는 P3로 표시되었고, VP는 대신 3개 기능 출시 우선 - 5번 연속 새벽 3시 호출 후 P0으로 변경, 8일 내 수정 - 나중에 해당 VP는 "엔지니어들이 알림 피로에 대해 과장한다고 생각했음. 아니었음" 이라고 인정함 4. 임원 보상에 인재 리텐션 지표 반영 - 경영진 변동 보상의 25%가 시니어 엔지니어 유지율에 의존하도록 구조 개편 - "시니어" 정의: 재직 기간 2년 이상, 성과 평가에서 기대치를 뛰어넘는 경우, 또는 핵심 시스템 담당 - 목표 설정: 시니어 엔지니어 연간 90% 유지 - 목표 미달 시 보너스 비례적으로 삭감 - 목표 초과 시 보너스가 배율로 지급 - 사례: Series B SaaS 회사가 2021년 이 구조 시행 - 시니어 엔지니어 이탈 연간 28% - 경영진 초기 저항: "누군가가 더 나은 제안을 받는 것을 우리가 통제할 수는 없다"라며 반대함 - CEO 응답: "그러면 우리가 연봉 외에는 경쟁력이 없다는 것을 인정하는 셈. 개선하거나 보상 영향 수용하라" - 1년 내 이탈률 11%로 감소 - 퇴사 인터뷰 패턴 전환: 떠나는 엔지니어들이 기능장애 기반 이탈(무시된 기술 우려, 성장 부재, 문화 독성)이 아닌 기회 기반 이탈(더 큰 회사 수석 승진, 스타트업 창업, 이전) 언급 - 경영진이 이제 인재 유지에 대한 책임감을 갖게 되면서, 보너스 목표 달성은 가장 쉬운 부분이 되어버림 5. 실질 권한을 가진 기술 자문 위원회(TAB) - 엔지니어링 조직이 선출한(경영진 임명 아닌) 시니어 엔지니어 5명으로 위원회 구성 - C 레벨들과 분기별 미팅 - 하나의 권한: 분기당 경영진 결정 1건 거부 가능 - 요구사항: 거부 시 기술적 정당화, 예상 비용, 위험 분석 포함 서면 대안 제안 필수 - 경영진은 CEO 승인과 문서화된 이유로만 거부권 무효화 가능 - 사례: 블록체인 인프라 회사가 2020년 초 TAB 구성 - 2년간 거부권 2회 행사 - 첫 번째 거부: 독점 합의 프레임워크 구축 결정 차단, 대신 기존 오픈소스 프로토콜 확장 제안. 추정 18개월 개발 시간 절약 - 두 번째 거부: 포괄적 롤백 테스트 없이 데이터베이스 마이그레이션 출시 방지. 사후 구현 분석에서 TAB이 200만 달러 데이터 손상 사고 예방 추정 - 진짜 영향은 더 미묘: 경영진이 기술 결정 최종화 전 "TAB이 이것 승인할까?" 묻기 시작 - 거부 위협이 TAB 도달 전 제안 품질 변화 - 엔지니어들이 기술적 판단이 마침내 경영진 결정에 중요해졌다고 보고 6. 보상 동등성을 갖춘 IC(개인 기여) 트랙 - IC 경력 발전 경로를 명확하게 설정 : Staff Engineer, Principal Engineer, Distinguished Engineer - 보상 밴드가 각각 Director, VP, SVP 수준과 일치해야 함 - 승진 기준: 팀 규모나 보고 체계보다는 기술적 영향력, 아키텍처에 대한 주도권, 다른 엔지니어들의 업무 효율성을 높이는 파급 효과 - 사례: 핀테크 회사가 6개월 내 Staff급 엔지니어 3명 이탈 - 퇴사 인터뷰에서 동일 패턴: "매니저가 되지 않고는 L7 보상에 도달 불가. 관리하고 싶지 않고 개발자가 되고 싶음" - 회사가 보상 동등성 있는 IC 트랙 시행 - 1년 내: 이전에 면접 보던 엔지니어 2명 Principal로 승진, 유사 경력 경로 없는 경쟁사에서 시니어 IC 3명 채용, 시니어 기술 이탈 62% 감소 - 더 중요하게, 회사에 남은 엔지니어들이 추정 300만 달러에 달하는 아키텍처 실수 예방 이는 주니어 또는 미드 레벨 엔지니어들이 전문성이나 권한 부족으로 이의를 제기할 수 없었던 결정들

실행 경로 (Implementation Paths)

- 실행 일정은 조직이 처한 심각도에 따라 달라짐 - 위기 상황 회사 (시니어 이탈률 >20%, 최근 중대 장애 발생) - 1-2주차: 실제 12개월 이탈 비용 계산(채용비, 생산성 적응 시간, 프로젝트 지연, 부족 지식 손실 포함), 퇴사 인터뷰 패턴 분석, 프로덕션 사고를 이전에 무시된 경고에 매핑 - 3-4주차: CFO와 CEO에게 발견 사항 제시, 패턴 표시(기술 우려 제기 → 우선순위 하향 → 엔지니어 이탈 → 사고 또는 비용), 총 손해 정량화, 즉각 개입 제안 - 5-8주차: 경영진 온콜 로테이션 시작(가장 빠른 문화 전환), 이탈비용 추적 시작(지속적 변화 케이스 구축), 엔지니어 3명으로 TAB 파일럿 생성, 경영진 대시보드에 월별 이탈 비용 추적 시작 - 9-12주차: 이사회에 보상 구조 변경 제시, 경영진 보너스를 유지율에 연계, IC 경력 트랙 공개 발표, 무엇이 왜 바뀌었는지 투명하게 소통 - 조기 경고 징후 회사 (이탈률 12-18%, 엔지니어들이 1:1에서 우려 언급) - 1-2개월차: 이탈 비용 추적 및 경제적 케이스 구축 시작, 유지 위험과 무엇이 머물게 할지 엔지니어 설문, 가장 자주 언급된 3가지 우려 식별 - 3-4개월차: 자원 경영진과 경영진 온콜 로테이션 파일럿, TAB 파일럿 시작, 둘 다 기술 부채와 조직 마찰 표면화에 사용, 이연된 작업 비용 문서화 - 5-6개월차: 영구 보상 구조 변경 시행, TAB 권한 공식화, IC 경력 트랙 기준 및 보상 밴드 공개, 시니어 엔지니어 유지를 명시적 경영진 목표로 설정

이것이 효과 없는 경우

이러한 개입은 3가지 시나리오에서 예측 가능하게 실패함, 이를 인정하지 않으면 시간만 낭비하게 됨

1. 애초에 이탈을 전제로 설계된 비즈니스 모델 - 컨설팅 회사와 계약 업체는 연간 20-40% 이직률 예상 - 비즈니스 모델이 인력 대체 비용을 가격에 반영하고, 청구 요율은 조직 내 전문 지식이 제한적이라는 전제하에 책정 - 제품 회사용으로 설계된 인력 유지 전략은 클라이언트 로테이션이 자연스러운 이탈을 유도하고 파트너 트랙이 의도적으로 업앤아웃 압력 생성하는 곳에서 의미 없음 - 유사하게 제품-시장 적합성 이전 초기 스타트업은 유지 실패가 아닌 필요한 피벗을 신호하는 엔지니어 이탈 경험 가능 - 회사가 6개월마다 근본적으로 방향을 전환하고 있다면, 낮은 유지율은 체계적 역기능이 아닌 적절한 인재 재배치를 의미 2. 실행하는 척만 하는 경우 (Implementation Theater) - 형식만 있는 개입은 무개입보다 더 나쁜 결과를 낳음 - 실제 거부 권한 없는 TAB은 엔지니어 우려를 분산시키는 배출구가 됨 - 체계적으로 무시되는 제안에 시간 투자 시 분노만 더 커짐 - 근본적인 원인 해결과 연결되지 않은 임원진의 온콜 로테이션은 책임감 없는 보여주기식 공감 생성 - 자신이 우선순위를 정해 해결할 수 없는 문제로 호출을 받는 VP는 엔지니어들이 자주 불평한다는 것만 학습 - 계산되지만 경영진 대시보드나 보상 논의에 절대 등장하지 않는 이탈비용 회계는 이론적인 논의로 남음 절반 시행된 개입은 리더십이 구조적 변화에 대한 진정한 의지 없이 관심을 보이는 척만 한다는 것을 보여줌 3. 문화적 전제조건 부재 - 이러한 개입은 많은 조직에 부재한 문화적 전제조건을 필요로 함: 리더십이 평판 관리가 아닌 진정한 행동 변화를 원해야 함 - 경영진이 엔지니어 유지를 경제적 문제가 아닌 PR 문제로 보면 가장 가시적인 개입(자문위원회, 청취 투어)만 시행하면서 비용 많이 드는 것(보상 구조조정, 실제 거부 권한)은 회피 - 진단 테스트: 경영진 변동 보상의 25%를 시니어 엔지니어 유지에 연계를 제안해 볼 것 - 리더십이 즉시 "우리 회사에서 안 되는 이유"를 제시한다면 해답을 찾은 것 - 그들은 개인적 비용 없는 솔루션을 원하는 것임 - 엔지니어에게 거부 권한 부여, 경영진 급여를 유지에 연계, 분기 재무 검토에 이탈 비용 반영할 준비가 안 된 회사는 구조적 변화 준비 안 됨 - 우려를 인정하고 "추가 연구" 권장하며 시니어 엔지니어 계속 이탈하는 동안 먼지 쌓이는 컨설팅 보고서에 만족할 뿐 - 개입은 리더십이 연간 140만 달러 이직 비용이 이를 막기 위해 필요한 조치보다 더 크다는 사실을 인식할 때 작동 - 그 인식 없으면 아무리 많은 자문위원회도 경제적 정렬을 대체 불가

새로운 경제적 계산

- 저자가 이끈 블록체인 인프라 회사가 3년간 10명에서 187명 엔지니어로 확장하면서도 연간 시니어 엔지니어 이탈률을 평균 6% 로 유지했는데, 초고속 성장 기업의 일반적인 이직률인 35~40%를 훨씬 웃도는 수치 - 성과의 원인은 복지나 문화적 장치가 아니라 인센티브 구조의 재설계에 있었음 - 중간 관리자가 모든 것이 통제 하에 있는 것처럼 보이는 것이 아닌 기술 위험 조기 표면화로 보상을 받음 - 사후분석에 이전 경고 문서화 요구; 무시된 경고는 우선순위를 낮춘 담당자의 성과 평가 대상이 됨 - 기술 리더십이 아키텍처 결정에 거부 권한 보유. 우리는 2회 사용했는데, 거부권 행사 가능성만으로도 전반적인 제안서 품질이 향상 - 창립 때부터 IC 경력 트랙 존재; 가장 시니어 비매니저가 대부분의 디렉터보다 더 많은 급여를 수령 - 시스템 비용: 보상 조정, 거버넌스 오버헤드, 일부 기능 지연한 기술 부채 우선순위 지정에 연간 약 $400,000 - 절감액: - 예방된 이탈 비용 210만 달러 (업계 표준 35% 이탈을 시니어 엔지니어 인원에 적용) - 추가로 시니어 엔지니어가 중단 권한이 있어 수백만 달러 사고를 만들지 않은 아키텍처 결정에서 측정 불가하지만 상당한 절감효과가 있음

불편한 진실

- 대부분의 회사는 어쩔 수 없이 강제적인 상황이 닥치기 전까지 이러한 개입 시행 안 함 - 강제 기능은 보통 재앙적인 상황임: 수백만 달러 비용 프로덕션 사고, 핵심 팀을 마비시키는 대규모 인력 이탈, 또는 경쟁사가 당신이 거부했던 것, 즉 기술적 판단에 대한 존중을 제시하며 당신의 핵심 엔지니어 인력의 절반을 빼앗아 가는 경우 - 그때쯤이면 예방이 아닌 피해 복구에 매달리게 됨 - 회복은 비용이 많이듬. 다음 위기 예방할 수 있는 최고 엔지니어들이 이미 떠났기 때문 - 그들의 후임자들은 재능은 있지만 조직에 대한 지식이 부족해서 어떤 경고를 해야 할지 모르며, 파멸 루프 가속화 - 문제는 이러한 개입이 효과 있는지가 아님, 증거는 명확함 - 경영진의 인센티브를 직원 유지에 맞춰 조정하, 엔지니어에게 의미 있는 권한 부여하고, 이직을 경제적 문제로 다루는 회사가 직원 유지율, 사고 발생율, 장기적인 기술 건전성 측면에서 일관되게 우수 - 숙련된 엔지니어들이 이탈하고 있고 일반적인 해결책이 효과가 없다면, 문제는 소통이 아니라 경제적인 측면일 수 있음

소프트웨어 공학의 법칙들 (lawsofsoftwareengineering.com)

- 소프트웨어 시스템, 팀, 의사결정에 영향을 미치는 56가지 원칙과 패턴을 한곳에 모은 컬렉션으로, 팀 운영부터 아키텍처, 품질, 설계, 의사결정까지 폭넓은 영역을 포괄 - Conway의 법칙, Brooks의 법칙, Dunbar의 수 등 팀 관련 법칙들은 조직 구조가 시스템 설계와 생산성에 직접적으로 영향을 미침 - 아키텍처 영역에서는 Hyrum의 법칙, CAP 정리, Gall의 법칙 등 복잡한 시스템 설계 시 반드시 고려해야 할 제약과 원칙을 정리 - 품질 관련 법칙들은 기술 부채, 테스팅 피라미드, Kernighan의 법칙 등 코드 품질 유지와 디버깅의 현실적 어려움을 다룸 - 의사결정 영역에서는 Dunning-Kruger 효과, 매몰 비용 오류, 파레토 원칙 등 개발 과정에서 빠지기 쉬운 인지 편향과 판단 기준을 포괄

팀(Teams)

  1. Conway의 법칙 (Conway's Law)

    • 조직은 자신의 커뮤니케이션 구조를 그대로 반영하는 시스템을 설계한다
    • 소프트웨어 아키텍처는 그것을 만든 조직의 소통 구조를 자연스럽게 따라가는 현상
    • 팀이 3개로 나뉘어 있으면 시스템도 3개의 큰 모듈로 나뉘는 경향이 있음
    • 이를 역으로 활용하는 "역 Conway 전략"(Inverse Conway Maneuver)도 존재: 원하는 아키텍처에 맞춰 팀 구조를 먼저 재편하는 접근
    • 마이크로서비스 도입 시 팀 경계와 서비스 경계를 일치시키는 것이 효과적
  2. Brooks의 법칙 (Brooks's Law)

    • 지연된 소프트웨어 프로젝트에 인력을 추가하면 오히려 더 늦어진다
    • 신규 인력이 합류하면 기존 팀원이 교육과 조율에 시간을 소모하여 전체 생산성이 일시 저하
    • 팀원이 늘어나면 커뮤니케이션 경로가 기하급수적으로 증가 (n명일 때 n(n-1)/2개)
    • Frederick Brooks가 IBM OS/360 프로젝트 경험을 바탕으로 1975년 저서 The Mythical Man-Month에서 정립
    • Little의 법칙(L = λ × W)으로 정량적 설명 가능: 인력 추가 시 WIP(작업 중인 항목)는 증가하지만 처리량(throughput)은 정체되어 리드 타임이 오히려 증가
    • 해결책은 인력 추가가 아닌 범위 조정 또는 일정 변경
  3. Dunbar의 수 (Dunbar's Number)

    • 한 사람이 안정적으로 유지할 수 있는 관계의 인지적 한계는 약 150명
    • Robin Dunbar가 영장류 뇌 크기와 사회 집단 규모의 상관관계에서 도출한 수치
    • Dunbar의 사회적 계층 구조: ~5명(친밀한 관계), ~15명(신뢰할 수 있는 협력자), ~50명(가까운 업무 관계), ~150명(안정적 사회적 연결)
    • 엔지니어링 조직이 150명을 넘으면 비공식적 소통이 한계에 도달하여 공식적 계층과 프로세스 필요
    • Amazon의 "Two-Pizza Team"(5~10명)은 150명 내에서도 실질적 협업은 더 작은 단위에서 일어남을 반영
  4. Ringelmann 효과 (The Ringelmann Effect)

    • 그룹 규모가 커질수록 개인의 생산성은 감소한다
    • 그룹 구성원이 많아질수록 각 개인의 기여도가 줄어드는 "사회적 태만"(social loafing) 현상
    • 소프트웨어 팀에서도 팀 크기가 커지면 개별 책임감이 희석되고 조율 비용이 증가
    • 소규모 팀이 1인당 더 높은 산출물을 내는 이유를 설명
  5. Price의 법칙 (Price's Law)

    • 전체 참여자 수의 제곱근에 해당하는 인원이 전체 작업의 50%를 수행한다
    • 100명의 조직에서는 약 10명이 전체 작업의 절반을 담당
    • 조직이 커질수록 소수의 고성과자에 대한 의존도가 심화
    • 팀 확장 시 생산성이 선형으로 증가하지 않는 이유를 설명
  6. Putt의 법칙 (Putt's Law)

    • 기술을 이해하는 사람은 관리하지 않고, 관리하는 사람은 기술을 이해하지 못한다
    • 기술 조직에서 관리 역할과 기술 전문성의 괴리를 풍자적으로 표현
    • 기술 리더십 구조를 설계할 때 이 간극을 인식하고 보완 장치를 마련해야 함
  7. Peter 원칙 (Peter Principle)

    • 조직 내에서 모든 직원은 자신의 무능력 수준까지 승진하는 경향이 있다
    • 특정 역할에서 유능한 사람이 승진하여 새로운 역할에서는 무능력해지는 패턴
    • 뛰어난 개발자가 반드시 좋은 매니저가 되지는 않는 현실을 반영
    • IC(Individual Contributor) 트랙과 매니지먼트 트랙을 분리하는 듀얼 래더 체계의 필요성
  8. Bus Factor

    • 프로젝트가 심각한 위험에 처할 수 있는 최소 팀원 이탈 수
    • Bus Factor가 1이면 단일 장애점(Single Point of Failure)이 존재하는 상태
    • 지식 공유, 페어 프로그래밍, 문서화를 통해 Bus Factor를 높이는 것이 중요
    • 코드 리뷰와 크로스 트레이닝이 Bus Factor를 개선하는 실질적 방법
  9. Dilbert 원칙 (Dilbert Principle)

    • 기업은 무능한 직원을 관리직으로 승진시켜 피해를 제한하는 경향이 있다
    • Scott Adams가 제안한 풍자적 관찰로, Peter 원칙의 변형
    • 관리직이 실무에서 가장 적은 피해를 주는 자리로 여겨지는 역설적 조직 현상

계획(Planning)

  1. 조기 최적화 / Knuth의 최적화 원칙 (Premature Optimization)

    • 조기 최적화는 모든 악의 근원이다
    • Donald Knuth가 1974년 논문에서 제시: "약 97%의 경우에서 작은 효율성은 무시해야 한다"
    • 코드의 약 20%가 실행 시간의 80%를 차지하므로, 나머지 80% 코드를 최적화하는 것은 낭비
    • 올바른 순서: 먼저 동작하게 만들고 → 올바르게 만들고 → 필요하면 빠르게 만들 것
    • 최적화된 코드는 복잡성이 높아지므로, 프로파일링을 통해 실제 병목 지점을 확인한 후 수행해야 함
  2. Parkinson의 법칙 (Parkinson's Law)

    • 업무는 주어진 시간을 모두 채울 때까지 확장한다
    • 데드라인이 2주면 2주, 4주면 4주 동안 일이 늘어나는 현상
    • 소프트웨어 프로젝트에서 짧고 명확한 마일스톤 설정이 중요한 이유
    • 스프린트 기반 애자일이 이 법칙에 대한 실질적 대응 방법
  3. 90-90 법칙 (The Ninety-Ninety Rule)

    • 코드의 처음 90%가 개발 시간의 90%를 차지하고, 나머지 10%가 또 다른 90%의 시간을 차지한다
    • 소프트웨어 프로젝트의 마지막 10%(엣지 케이스, 폴리싱, 버그 수정)가 예상보다 훨씬 오래 걸림을 경고
    • "거의 완성"이라는 말이 실제로는 전체 일정의 절반 지점일 수 있음
  4. Hofstadter의 법칙 (Hofstadter's Law)

    • Hofstadter의 법칙을 고려하더라도 항상 예상보다 오래 걸린다
    • 재귀적 자기 참조 구조를 가진 법칙으로, 소프트웨어 일정 추정의 본질적 어려움을 표현
    • 버퍼를 추가해도 여전히 일정이 초과되는 현실
    • Douglas Hofstadter가 1979년 저서 Gödel, Escher, Bach에서 소개
  5. Goodhart의 법칙 (Goodhart's Law)

    • 측정 지표가 목표가 되면 더 이상 좋은 측정 지표가 아니다
    • 코드 커버리지를 KPI로 설정하면 의미 없는 테스트를 양산하는 현상이 대표적 사례
    • 코드 라인 수(LOC)로 생산성을 측정하면 불필요하게 장황한 코드가 생산됨
    • 지표 최적화가 아닌 본질적 가치 달성에 집중해야 함
  6. Gilb의 법칙 (Gilb's Law)

    • 정량화가 필요한 것은 측정하지 않는 것보다 어떤 방식으로든 측정하는 것이 나다
    • 완벽한 측정이 불가능하더라도 대략적 측정이 무측정보다 항상 유익
    • 소프트웨어 품질, 사용자 만족도 등 정량화가 어려운 항목에도 적용

아키텍처(Architecture)

  1. Hyrum의 법칙 (Hyrum's Law)

    • API 사용자가 충분히 많으면, 시스템의 모든 관찰 가능한 동작에 누군가 의존한다
    • 공식 API 명세뿐 아니라 타이밍, 에러 메시지 형식, 정렬 순서 등 비공식적 동작도 의존 대상이 됨
    • Microsoft Windows가 과거 문서화되지 않은 동작과 버그에 의존하는 서드파티 앱 호환을 위해 구버전 동작을 유지한 사례
    • Google의 Hyrum Wright가 2011-2012년경 Google 내부 라이브러리 변경 경험에서 관찰
    • 동료 Titus Winters가 "Hyrum's Law"라는 이름을 붙임 (Software Engineering at Google 수록)
    • 실질적 계약은 공식 API가 아니라 실제 관찰되는 동작 전체
  2. Gall의 법칙 (Gall's Law)

    • 작동하는 복잡한 시스템은 반드시 작동하는 단순한 시스템에서 진화한 결과이다
    • 처음부터 복잡한 시스템을 설계하면 검증되지 않은 미지의 변수가 너무 많아 실패 확률이 높음
    • MVP(Minimum Viable Product) 접근의 이론적 근거
    • Facebook이 2004년 Harvard 학생용 단순 프로필 시스템에서 시작하여 점진적으로 확장한 사례
    • 마이크로서비스 전환 시에도 모놀리스에서 시작하여 점진적으로 분리하는 것이 유리
    • John Gall이 1975년 저서 Systemantics에서 제시 (30개 출판사가 거절 후 출간된 컬트 클래식)
  3. 누수 추상화의 법칙 (The Law of Leaky Abstractions)

    • 모든 비자명(non-trivial) 추상화는 어느 정도 누수가 발생한다
    • ORM이 SQL을 숨기지만 성능 문제가 발생하면 결국 생성되는 쿼리를 확인해야 하는 상황이 대표 사례
    • Java/Python의 가비지 컬렉션도 추상화이지만 GC 일시정지 같은 내부 동작이 성능에 영향을 미침
    • 추상화 자체가 나쁜 것이 아니라, 추상화가 깨질 때를 대비해야 한다는 교훈
    • Joel Spolsky가 2002년 블로그 포스트에서 TCP, 가상 메모리 등의 사례와 함께 소개
    • George Box의 "모든 모델은 틀렸지만, 일부는 유용하다"와도 맥락 연결
  4. Tesler의 법칙 / 복잡성 보존 법칙 (Tesler's Law)

    • 모든 애플리케이션에는 제거할 수 없는 고유 복잡성이 있으며, 이동만 가능하고 제거는 불가능하다
    • 핵심 질문: 복잡성을 누가 감당할 것인가 (사용자 vs 시스템)
    • Calendly는 일정 조율의 복잡성을 시스템이 흡수하고, 이메일 스레드는 사용자에게 전가하는 차이
    • 좋은 설계는 복잡성을 사용자 경험에서 시스템 내부로 이동시킴
    • Larry Tesler가 Apple Lisa 및 초기 GUI 작업 중 1980년대에 정립
  5. CAP 정리 (CAP Theorem)

    • 분산 시스템은 일관성©, 가용성(A), 분할 내성℗ 중 두 가지만 보장 가능하다
    • 네트워크 파티션은 현실에서 불가피하므로, 실질적 선택은 일관성 vs 가용성
    • CP 시스템 (예: MongoDB): 파티션 발생 시 쓰기를 차단하여 모든 레플리카 동기화 유지
    • AP 시스템 (예: Cassandra, DNS): 파티션 중에도 요청 응답 유지, 레플리카 간 일시적 불일치 허용
    • Eric Brewer가 2000년에 웹 서비스 맥락에서 제안, Gilbert & Lynch가 2002년에 공식 증명
  6. Second-System 효과 (Second-System Effect)

    • 작고 성공적인 시스템 다음에는 과도하게 설계된 비대한 후속 시스템이 뒤따르는 경향
    • 첫 번째 시스템의 성공에 자신감을 얻어 두 번째 시스템에 모든 아이디어를 쏟아붓는 패턴
    • 기능 과잉(feature creep)과 과도한 일반화(over-generalization)가 주된 원인
    • Frederick Brooks가 The Mythical Man-Month에서 식별
  7. 분산 컴퓨팅의 오류 (Fallacies of Distributed Computing)

    • 분산 시스템을 처음 설계하는 사람들이 흔히 갖는 8가지 잘못된 가정
    • 8가지 오류: (1) 네트워크는 신뢰할 수 있다, (2) 지연 시간은 0이다, (3) 대역폭은 무한하다, (4) 네트워크는 안전하다, (5) 토폴로지는 변하지 않는다, (6) 관리자가 한 명이다, (7) 전송 비용은 0이다, (8) 네트워크는 균일하다
    • 이 가정들을 기반으로 설계하면 프로덕션에서 예기치 않은 장애와 성능 문제가 발생
  8. 의도하지 않은 결과의 법칙 (Law of Unintended Consequences)

    • 복잡한 시스템을 변경하면 예기치 않은 결과를 예상해야 한다
    • 시스템 하나의 컴포넌트를 변경하면 예측하지 못한 곳에서 부작용이 발생
    • 카오스 엔지니어링과 포괄적 테스팅의 필요성을 뒷받침하는 원칙
  9. Zawinski의 법칙 (Zawinski's Law)

    • 모든 프로그램은 메일을 읽을 수 있을 때까지 확장을 시도한다
    • 소프트웨어가 성공하면 점점 더 많은 기능을 추가하려는 기능 팽창(feature bloat) 현상을 풍자
    • Jamie Zawinski(Netscape 초기 개발자)가 관찰
    • 단순한 도구가 시간이 지나면 만능 플랫폼이 되려 하는 경향에 대한 경고

품질(Quality)

  1. Boy Scout 규칙 (The Boy Scout Rule)

    • 코드를 발견한 것보다 더 나은 상태로 남겨야 한다
    • 대규모 리팩토링이 아닌 지속적이고 점진적인 개선이 핵심
    • 혼란스러운 함수명 수정, 중복 코드 제거, 누락된 테스트 추가 등 매번 작은 개선을 실천
    • Robert C. Martin(Uncle Bob)이 Clean Code(2008)에서 소프트웨어 개발에 적용
    • Google 엔지니어들의 원칙 "If you touch it, you own it" — 코드를 수정하면 그 품질에 대한 책임도 함께 가져감
    • 이 규칙을 실천하면 깨진 유리창 효과(Broken Windows)를 예방하고 기술 부채 축적을 방지
  2. Murphy의 법칙 (Murphy's Law)

    • 잘못될 수 있는 것은 반드시 잘못된다
    • 방어적 프로그래밍, 예외 처리, 장애 대비 설계의 근거
    • 소프트웨어에서는 "일어날 수 있는 에러는 반드시 일어난다"는 자세로 에러 핸들링과 폴백을 설계해야 함
  3. Postel의 법칙 / 견고성 원칙 (Postel's Law)

    • 자신이 하는 일에는 보수적으로, 타인으로부터 받는 것에는 관대하게
    • API 설계 시 출력은 엄격하게 스펙을 준수하되, 입력은 다양한 형식을 유연하게 수용하는 원칙
    • Jon Postel이 TCP/IP 프로토콜 설계 시 수립한 견고성 원칙(Robustness Principle)
    • 시스템 간 상호운용성을 높이는 실질적 가이드라인
  4. 깨진 유리창 이론 (Broken Windows Theory)

    • 나쁜 설계, 잘못된 결정, 저품질 코드를 방치하지 말 것
    • 하나의 "깨진 유리창"(나쁜 코드)이 방치되면 추가적인 품질 저하를 유발
    • 코드베이스에 TODO 주석, 죽은 코드, 미해결 경고가 쌓이면 새로운 코드도 낮은 수준으로 작성되는 경향
    • 발견 즉시 작은 문제라도 수정하는 문화가 중요
  5. 기술 부채 (Technical Debt)

    • 소프트웨어 개발 속도를 저하시키는 모든 요소
    • Ward Cunningham이 1992년 OOPSLA에서 금융 메타포로 처음 사용: 코드 지름길을 택하면 미래에서 시간을 빌리는 것
    • 원금(수정 비용) + 이자(지저분한 코드로 인한 지속적 생산성 저하)
    • 의도적 기술 부채는 때로 합리적 (시장 출시 타이밍, 프로토타이핑)이지만 상환 계획이 필수
    • 자동화 테스트 생략이 대표적 사례: 릴리스는 성공하지만 이후 변경 시마다 예상치 못한 버그 발생
    • 해결 방법: 리팩토링, 누락 테스트 추가, 설계 개선
  6. Linus의 법칙 (Linus's Law)

    • 충분한 수의 검토자가 있으면 모든 버그는 쉽게 발견된다
    • 오픈소스 개발의 핵심 원리: 다수의 눈이 코드를 검토하면 버그가 사소한 문제가 됨
    • Eric Raymond가 The Cathedral and the Bazaar에서 Linus Torvalds의 이름을 따 명명
    • 코드 리뷰 문화의 중요성을 뒷받침
  7. Kernighan의 법칙 (Kernighan's Law)

    • 디버깅은 코드를 처음 작성하는 것보다 두 배 어렵다
    • 따라서 최대한 영리하게 코드를 작성하면, 디버깅할 때 충분히 영리하지 못하게 됨
    • 가독성 높은 단순한 코드를 작성해야 하는 이유
    • Brian Kernighan이 The Elements of Programming Style에서 제시
  8. 테스팅 피라미드 (Testing Pyramid)

    • 프로젝트는 빠른 단위 테스트를 많이, 통합 테스트는 적게, UI 테스트는 소수만 보유해야 한다
    • 단위 테스트(하단): 빠르고 저비용, 가장 많이 작성
    • 통합 테스트(중간): 컴포넌트 간 상호작용 검증
    • UI/E2E 테스트(상단): 느리고 깨지기 쉬우므로 최소화
    • Mike Cohn이 Succeeding with Agile에서 소개한 테스트 전략 모델
  9. 살충제 역설 (Pesticide Paradox)

    • 동일한 테스트를 반복 실행하면 시간이 지남에 따라 효과가 감소한다
    • 기존 테스트가 이미 잡을 수 있는 버그는 다 잡았으므로, 새로운 테스트 케이스를 지속적으로 추가해야 함
    • 테스트 세트를 정기적으로 검토하고 업데이트하는 것이 필수
  10. Lehman의 소프트웨어 진화 법칙 (Lehman's Laws of Software Evolution)

    • 현실 세계를 반영하는 소프트웨어는 반드시 진화해야 하며, 그 진화에는 예측 가능한 한계가 존재한다
    • E-type(현실 세계를 반영하는) 소프트웨어는 사용되려면 지속적 변경이 불가피
    • 변경 시마다 복잡성이 증가하며, 이를 적극적으로 관리하지 않으면 품질이 저하
  11. Sturgeon의 법칙 (Sturgeon's Law)

    • 모든 것의 90%는 쓸모없다
    • Theodore Sturgeon이 SF 문학 비평에 대응하여 제시
    • 소프트웨어에도 적용: 대부분의 코드, 도구, 프레임워크 중 진정으로 훌륭한 것은 소수
    • 품질에 대한 높은 기준을 유지하고, 가치 있는 10%에 집중하는 자세 필요

스케일(Scale)

  1. Amdahl의 법칙 (Amdahl's Law)

    • 병렬화로 인한 속도 향상은 병렬화할 수 없는 작업 비율에 의해 제한된다
    • 프로그램의 5%가 순차적이면 아무리 많은 프로세서를 투입해도 이론적 최대 속도 향상은 20배
    • 병렬화의 한계를 인식하고 순차적 병목 구간을 줄이는 것이 더 효과적
    • Gene Amdahl이 1967년에 제시
  2. Gustafson의 법칙 (Gustafson's Law)

    • 문제 크기를 늘림으로써 병렬 처리에서 상당한 속도 향상을 달성할 수 있다
    • Amdahl의 법칙에 대한 보완적 관점: 고정된 문제가 아닌 확장 가능한 문제에서는 프로세서 추가가 효과적
    • 빅데이터 처리, 과학 시뮬레이션 등에서 더 많은 리소스로 더 큰 문제를 풀 수 있음
  3. Metcalfe의 법칙 (Metcalfe's Law)

    • 네트워크의 가치는 사용자 수의 제곱에 비례한다
    • 사용자가 10명이면 가치는 100 단위, 100명이면 10,000 단위로 증가
    • 소셜 네트워크, 메신저, 마켓플레이스 등 네트워크 효과의 이론적 기반
    • Robert Metcalfe가 이더넷 기술의 가치를 설명하기 위해 제시

설계(Design)

  1. DRY 원칙 (Don't Repeat Yourself)

    • 모든 지식은 단일하고 명확하며 권위 있는 하나의 표현만 가져야 한다
    • 코드 중복뿐 아니라 지식, 로직, 데이터의 중복도 포함
    • 중복은 변경 시 여러 곳을 동시에 수정해야 하므로 버그와 불일치의 원인
    • Andy Hunt와 Dave Thomas가 The Pragmatic Programmer에서 정립
  2. KISS 원칙 (Keep It Simple, Stupid)

    • 설계와 시스템은 가능한 한 단순해야 한다
    • 복잡성은 이해, 유지보수, 디버깅의 비용을 증가시킴
    • 단순한 해결책이 대부분의 경우 더 효과적이며 결함 가능성도 낮음
    • 미국 해군이 1960년대에 제시한 설계 원칙에서 유래
  3. SOLID 원칙 (SOLID Principles)

    • 소프트웨어 설계를 향상시키는 5가지 핵심 가이드라인
    • S — 단일 책임 원칙(Single Responsibility): 클래스는 하나의 이유로만 변경
    • O — 개방-폐쇄 원칙(Open-Closed): 확장에 열려 있고 수정에 닫혀 있어야 함
    • L — Liskov 치환 원칙: 하위 타입은 상위 타입을 대체할 수 있어야 함
    • I — 인터페이스 분리 원칙: 클라이언트는 사용하지 않는 인터페이스에 의존하지 않아야 함
    • D — 의존성 역전 원칙: 상위 모듈이 하위 모듈에 의존하지 않고 추상화에 의존
    • Robert C. Martin이 정립하고 Michael Feathers가 SOLID라는 약어를 명명
  4. 디미터 법칙 (Law of Demeter)

    • 객체는 직접적인 친구와만 상호작용해야 하며, 낯선 객체와의 직접 소통은 지양해야 한다
    • a.getB().getC().doSomething() 같은 체인 호출을 피해야 한다는 원칙
    • 결합도를 낮추고 캡슐화를 강화하여 변경 영향 범위를 줄임
    • "최소 지식의 원칙"이라고도 불림
  5. 최소 놀라움의 원칙 (Principle of Least Astonishment)

    • 소프트웨어와 인터페이스는 사용자와 다른 개발자를 가장 적게 놀라게 하는 방식으로 동작해야 한다
    • 함수, API, UI가 이름과 컨벤션에서 예측 가능한 동작을 해야 함
    • delete() 함수가 실제로는 아카이브만 한다면 놀라움을 유발 → 설계 결함
    • 직관적이지 않은 동작은 버그와 사용자 실수를 초래
  6. YAGNI (You Aren't Gonna Need It)

    • 필요하기 전까지 기능을 추가하지 말 것
    • Extreme Programming(XP)의 핵심 원칙으로 1990년대 후반 Ron Jeffries가 제시
    • "미래에 필요할지 모른다"는 이유로 코드를 작성하면 과잉 설계와 유지보수 부담 발생
    • 리팩토링에 대한 자신감(좋은 테스트 커버리지, CI)이 있어야 YAGNI를 실천 가능
    • 현재 JSON 내보내기만 필요하면 JSON만 구현, XML/YAML 등은 요구될 때 추가

의사결정(Decisions)

  1. Dunning-Kruger 효과 (Dunning-Kruger Effect)

    • 어떤 것에 대해 적게 알수록 더 자신감을 갖는 경향이 있다
    • 초보 개발자가 복잡한 시스템의 난이도를 과소평가하거나, 전문가가 오히려 자신의 지식에 겸손한 현상
    • 코드 리뷰, 멘토링, 지속 학습을 통해 자기 인식의 정확성을 높이는 것이 중요
  2. Hanlon의 면도날 (Hanlon's Razor)

    • 어리석음이나 부주의로 충분히 설명되는 것을 악의로 돌리지 말 것
    • 동료의 나쁜 코드나 잘못된 결정을 의도적 방해로 해석하기 전에 무지, 실수, 시간 부족을 먼저 고려
    • 팀 내 신뢰와 건설적 커뮤니케이션의 기반
  3. Occam의 면도날 (Occam's Razor)

    • 가장 단순한 설명이 가장 정확한 경우가 많다
    • 디버깅 시 복잡한 원인보다 가장 단순한 가능성부터 먼저 확인
    • 아키텍처 설계에서도 불필요한 추상화 레이어를 추가하기 전에 단순한 해결책을 우선 탐색
  4. 매몰 비용 오류 (Sunk Cost Fallacy)

    • 시간이나 에너지를 투자했다는 이유만으로 손해가 되는 선택을 계속 유지하는 현상
    • 6개월 동안 개발한 기능이 잘못된 방향이었더라도 투자한 시간 때문에 버리지 못하는 심리
    • 올바른 결정은 과거 투자가 아닌 미래 가치를 기준으로 해야 함
  5. 지도는 영토가 아니다 (The Map Is Not the Territory)

    • 현실에 대한 표현(모델)은 현실 자체와 동일하지 않다
    • UML 다이어그램, 아키텍처 문서, 데이터 모델 등은 현실의 근사치일 뿐
    • 모델을 맹신하지 말고, 실제 시스템의 동작을 관찰하며 모델을 갱신해야 함
  6. 확증 편향 (Confirmation Bias)

    • 기존 믿음이나 아이디어를 지지하는 정보를 선호하는 경향
    • 자신이 선택한 기술 스택이나 설계 결정에 유리한 정보만 선별적으로 수집하는 함정
    • 반대 증거를 적극적으로 탐색하고 다양한 관점을 수용하는 것이 균형 잡힌 의사결정의 핵심
  7. Hype Cycle과 Amara의 법칙 (The Hype Cycle & Amara's Law)

    • 기술의 단기 효과는 과대평가하고, 장기 영향은 과소평가하는 경향이 있다
    • Gartner의 Hype Cycle: 기술 촉발 → 과도한 기대의 정점 → 환멸의 골짜기 → 계몽의 경사 → 생산성 안정기
    • 새 기술(블록체인, AI 등)을 도입할 때 단기 과열에 휩쓸리지 말고 장기적 실용성을 평가해야 함
  8. Lindy 효과 (The Lindy Effect)

    • 오래 사용된 것일수록 앞으로도 계속 사용될 가능성이 높다
    • UNIX, SQL, C 언어처럼 수십 년간 사용된 기술은 앞으로도 오래 생존할 가능성이 높음
    • 새로운 프레임워크보다 검증된 기술을 선택할 때의 이론적 근거
    • Nassim Nicholas Taleb이 Antifragile에서 대중화
  9. 제1원리 사고 (First Principles Thinking)

    • 복잡한 문제를 가장 기본적인 구성 요소로 분해한 후 그로부터 재구축하는 사고법
    • 기존 관행과 가정을 제거하고 근본적 진실에서 출발하여 해결책을 도출
    • Elon Musk가 SpaceX 로켓 비용 절감에 적용한 사례로 유명
    • 복잡한 시스템 설계 시 "원래 다 그렇게 하니까"라는 사고를 경계
  10. 역전 사고 (Inversion)

    • 반대 결과를 상정하고 거꾸로 추론하여 문제를 해결하는 방법
    • "어떻게 성공할까" 대신 "어떻게 하면 실패할까" 를 먼저 생각하여 위험 요소를 식별
    • 장애 모드 분석(Failure Mode Analysis)과 프리모템(Pre-mortem)의 이론적 근거
    • Charlie Munger가 자주 활용하는 멘탈 모델
  11. 파레토 원칙 / 80/20 법칙 (Pareto Principle)

    • 문제의 80%는 원인의 20%에서 발생한다
    • 전체 버그의 80%가 코드의 20%에 집중되어 있는 경향
    • 가장 영향력이 큰 20%에 리소스를 집중하는 것이 효율적 자원 배분 전략
    • Vilfredo Pareto가 이탈리아 토지 소유 분포에서 관찰한 원리에서 유래
  12. Cunningham의 법칙 (Cunningham's Law)

    • 인터넷에서 정확한 답을 얻는 가장 좋은 방법은 질문이 아니라 틀린 답을 게시하는 것이다
    • 사람들은 질문에 답하는 것보다 잘못된 정보를 교정하는 데 더 적극적으로 참여
    • Ward Cunningham(Wiki 발명자)의 이름을 딴 법칙이지만, 실제로는 Steven McGeady가 이 이름을 붙임
    • 오픈소스 커뮤니티에서 문서화나 지식 공유에 활용 가능한 통찰

AI 코딩 시대, 성장이 멈추는 개발자의 뇌에서 일어나는 일

TL;DR;

- AI를 잘 쓰는 핵심 역량은 출력물의 품질을 판단하고 교정하는 능력이며, 이 능력은 AI에 의존할수록 오히려 약화됨 - Bjork의 "바람직한 어려움" 이론에 따르면, 쉽게 처리한 정보는 장기 기억에 남지 않음 - Roediger & Karpicke(2006) 연구에서 인출 연습 그룹의 일주일 후 기억 보존율이 반복 읽기 그룹보다 약 50% 높았음 - AI가 코드를 대신 작성하면 본질적 인지 부하(germane load) 까지 제거되어 스키마 형성 기회 자체가 사라짐 - 숙련된 개발자일수록 신경 효율성으로 인해 AI 출력을 읽는 것만으로는 뇌에 걸리는 부하가 거의 없음 - AI 이전에도 성장이 멈추는 경로는 존재했지만, AI는 그 경로의 마찰을 극적으로 제거함

상세 요약

AI를 잘 쓰려면 코드를 알아야 하는 역설

- "이걸 만들어줘"라고 말할 수 있는 사람은 많지만, AI 결과물을 보고 "이 구조는 변경에 취약하다", "이 인터페이스는 두 가지 책임을 가지고 있다"고 구체적으로 교정할 수 있는 사람은 훨씬 적음 - 이 능력은 수많은 실패와 디버깅과 리팩토링 경험에서 형성된 직감에 가까움 - AI 사용법 학습과 코드 패턴 학습은 양자택일 관계가 아니라, 후자가 전자의 토대인 관계임 "AI를 가장 잘 활용할 수 있는 개발자는 AI 없이도 코드를 판단할 수 있는 개발자"

뇌는 편하면 기억하지 않는다

- Bjork의 "바람직한 어려움": 학습 과정에 적절한 난이도와 저항이 존재할 때 단기 수행은 느려지지만 장기 기억 보존과 전이는 향상됨 - Roediger & Karpicke(2006): 반복 읽기 vs 인출 연습 실험 - 5분 후 테스트: 반복 읽기 그룹 성적이 더 높음 - 일주일 후 재테스트: 인출 연습 그룹의 기억 보존율이 약 50% 더 높음 - 능동적 인출 그룹은 해마–전두엽 피질 연결성이 강화되고 감각운동 네트워크 활성도 증가 - 수동 학습 상태의 뇌는 해마–방추상회 연결만 활성화 — "정보를 보고 있을 뿐 처리하지 않는 것"에 가까움 - 생성 효과 (Slamecka & Graf, 1978): "뜨거운-차___" 처럼 직접 완성한 그룹이 완성된 쌍을 읽은 그룹보다 기억 보존율이 유의미하게 높음 - 유창성의 착각: 정보를 쉽게 처리할 수 있다는 느낌이 잘 기억할 것이라는 착각으로 이어짐

코딩 실력은 절차 기억이다

- 코딩 실력의 상당 부분은 절차 기억 — 자전거 타기처럼 한번 체화되면 의식하지 않아도 자동 실행됨 - Anderson의 적응적 사고 통제(ACT) 모델: 절차 기억 형성 3단계 - 인지 단계: 모든 것을 의식적으로 한 단계씩 실행, 작업 기억 대부분 소모 - 연합 단계: 개별 절차들이 통합되어 하나의 흐름으로 실행 가능 - 자동화 단계: 작업 기억 거의 차지하지 않고 자동 실행 — 남은 여유를 설계 판단에 활용 가능 - 단계 전환은 반복적인 직접 수행을 통해서만 진행됨 - 청킹 (Chase & Simon 체스 연구): 전문가와 초보자의 차이는 작업 기억 슬롯 수가 아니라, 하나의 청크에 담을 수 있는 정보의 양 체스 고수는 말의 개별 위치가 아닌 "시실리안 디펜스의 전형적인 중반 배치" 같은 의미 있는 패턴을 하나의 청크로 인식 무작위 배치 실험에서 고수와 초보자 차이가 사라짐으로써 입증됨

AI는 이 과정을 방해한다

- AI에게 구현을 맡기면 본질적 인지 부하(germane load) 까지 AI가 대신 처리 — 스키마 구축 기회 자체가 사라짐 - 절차 기억 관점: 인지 단계에서 끙끙대는 시간이 줄어 연합 단계 전환이 지연되고 자동화 단계 도달이 어려워짐 - AI 출력 코드를 읽는 것은 생성 효과 실험의 "완성된 단어 쌍을 읽는 것"에 해당 — 이해한 것 같지만 깊이 각인되지 않음 - 숙련된 개발자일수록 신경 효율성으로 코드를 더 적은 자원으로 처리 → AI 출력 읽기는 생각보다 뇌에 부하를 거의 걸지 않음 - 직접 코드를 짤 때는 예측–피드백 루프로 시냅스가 수정되지만, 완성된 AI 코드 읽기는 예측 과정이 생략된 사후 해석에 불과함 - 주니어에게 특히 심각: 인지 단계에 있는 패턴이 많은 상태에서 AI가 그 단계를 건너뛰게 하면 절차 기억 형성 없이 경력만 쌓임

뇌에 부하를 거는 방법

- AI에게 맡기기 전에 먼저 자신의 설계안 작성: 생성 효과를 의도적으로 활용 — AI 출력과 비교·평가하는 과정에서 뇌의 의미 처리와 실행 - 제어 영역이 동시 활성화 - 진지한 코드 리뷰: "왜 이 구조인가", "6개월 뒤에 수정한다면 어디가 문제가 될까"를 의식적으로 묻는 것 — 귀찮음 자체가 바람직한 - 어려움 - 직접 코드를 짜보는 시간 확보: 절차 기억 형성에 대체 불가능 — 막혔을 때는 전체 답이 아닌 최소한의 힌트만 AI에게 요청 - 생산과 학습의 최적 전략은 다름: AI는 생산 도구로는 탁월하지만, 학습 도구로는 한계가 있음 - 결국 뇌에 남은 것들이 코드 리뷰의 질, 설계 판단의 정확도, 역설적으로 AI 활용 능력을 결정함

Why 2026 Seniors are just highly-paid Code Editors, on Addy Osmani

Google Cloud AI 디렉터이자 전 Chrome 엔지니어링 리더인 Addy Osmani가 2026년 JS Nation US(뉴욕) 컨퍼런스 인터뷰에서 밝힌, AI 시대 시니어 개발자 역할의 본질적 변화에 대한 대담입니다. Osmani는 Learning JavaScript Design Patterns, Leading Effective Engineering Teams 등 14~15권의 기술서를 저술한 인물로, 2025년 "The AI-Native Software Engineer" 강연과 Substack의 "70% 문제", "80% 문제" 시리즈를 통해 AI 코딩의 현실적 한계를 꾸준히 짚어왔습니다. 이번 인터뷰는 그 연장선에서, 시니어 엔지니어가 코드 작성자에서 코드 편집자(Editor)로 전환되고 있는 현상을 다각도로 풀어냅니다.

AI 코딩 1년 후의 풍경

- 개발자 90%가 AI를 코딩에 활용하고 있으나, 신뢰도는 오히려 하락 추세에 있습니다 - 새 프로젝트나 프로토타입(MVP)에는 효과적이지만, 대규모 코드베이스나 엔터프라이즈 환경에서는 여전히 격차가 뚜렷합니다 - PR(풀 리퀘스트) 크기가 크게 증가하고 있으며, AI가 필요 이상의 파일을 건드리거나 기존 유틸리티 함수를 재활용하지 않고 새로 구현하는 경우가 빈번합니다 - Osmani가 이전 글에서 "70% 문제"로 명명한 현상은 여전히 유효합니다. AI가 70%까지 데려다 주지만 나머지 30%의 품질, 정합성, 라스트 마일(최종 마무리 작업)은 사람의 몫입니다

바이브 코딩 vs AI 보조 엔지니어링

- 바이브 코딩(Vibe Coding)은 아이디어의 실현 가능성을 빠르게 탐색하는 자유로운 방식으로, 코드 리뷰에 크게 신경 쓰지 않는 접근입니다 - AI 보조 엔지니어링은 아키텍처, 보안, 성능, 품질 등 전통적 엔지니어링 원칙을 유지하면서 AI를 도구로 활용하는 방식입니다 - 프로덕션 코드에는 후자가 필수이며, 이때 "컨텍스트 엔지니어링"(모델에 문서, 예시, 대화 이력, 코드베이스 구조 등 풍부한 맥락을 제공하는 기법)이 결과물 품질을 좌우합니다

시니어 엔지니어의 새 역할: 코드 에디터

- 개발자의 핵심 역할이 코드를 작성하는 사람에서 코드를 평가하고 편집하는 사람으로 이동하고 있습니다. 제목이 시사하는 "highly-paid Code Editors"의 의미가 여기에 있습니다 - 코드 리뷰가 주니어 교육의 핵심 현장이 되고 있으며, "AI가 왜 이 접근법을 선택했는가"를 따져 묻는 비판적 사고가 그 어느 때보다 중요합니다 - 한 연구에 따르면, 엔지니어들이 "겉보기엔 맞지만 실제로는 틀린" AI 코드를 디버깅하는 데 상당한 시간을 소모하고 있다고 합니다. Osmani는 이를 "이해도 부채(comprehension debt)"라는 개념으로 후속 글에서 확장한 바 있습니다

백그라운드 에이전트의 실전 활용

- Osmani는 산책 중 GitHub 앱으로 서너 개의 작업을 에이전트에 맡기고, 돌아올 때쯤 PR을 받는 방식을 쓰고 있습니다. "이슈가 아니라 PR을 원한다"는 표현이 인상적입니다 - 소규모~중규모 프로젝트에 한정하며, 엔터프라이즈에는 아직 권하지 않습니다 - 에이전트를 하나만 쓰는 "지휘자(conductor)" 단계에서 여러 에이전트를 동시에 관리하는 "오케스트레이터(orchestrator)" 단계로 전환되고 있다는 비유를 사용합니다

Chrome DevTools MCP와 Figma MCP

- 2025년 말 출시된 Chrome DevTools MCP(Model Context Protocol)는 코딩 에이전트에 "눈"을 부여합니다. 에이전트가 실제 렌더링 결과를 확인하고, 콘솔 로그와 네트워크 정보까지 활용할 수 있게 됩니다 - Figma MCP와 조합하면 디자인 파일을 구현한 뒤 실제 화면을 검증하는 흐름이 가능합니다. 다만 기존 UI 컴포넌트 라이브러리를 자동으로 재활용하는 수준에는 아직 도달하지 못했습니다

브라우저 AI의 미래와 신뢰 문제

- 브라우저가 보유한 풍부한 컨텍스트(로그인 정보, 캘린더, 검색 이력 등)를 활용한 사용자 여정 자동화가 다음 단계이지만, 결제나 개인정보가 관여되는 지점에서 사람의 확인을 유지하는 신뢰 설계가 관건입니다 - Osmani는 "100% 자동화가 아니라, 사용자가 눈썹을 치켜올릴 만한 단계에서 반드시 멈춰야 한다"고 강조합니다

주니어 개발자에 대한 조언

- AI가 아직 해결하지 못하는 영역에서 깊은 전문성을 쌓을 수 있다면 오히려 차별화의 기회입니다 - 프로그래밍 언어나 스택이 무의미해진다는 극단적 견해에 대해 Osmani는 "기초와 펀더멘탈을 이해하는 것이 여전히 초능력(superpower)"이라고 반박합니다

시사점

- 이 대담의 핵심 메시지는 명확합니다. AI가 코드를 대신 써주는 시대에, 시니어 엔지니어의 가치는 코드를 작성하는 속도가 아니라 코드를 읽고, 판단하고, 맥락을 부여하는 능력에 있습니다. "고연봉 코드 편집자"라는 다소 도발적인 제목은, 그것이 폄하가 아니라 오히려 이 시대가 요구하는 핵심 역량이라는 역설을 담고 있습니다 - Osmani가 70%에서 80%로 숫자를 올린 것처럼, 에이전트의 완성도는 분명 높아지고 있습니다. 그러나 나머지 20~30%를 메우는 "이해도 부채"의 관리 비용은 줄지 않았고, 이 격차를 줄이는 것이 앞으로 도구와 엔지니어 양쪽 모두의 과제로 남아 있습니다

컴퓨터공학 교육에서 빠진 학기 – 2026년 개정판 (missing.csail.mit.edu)

- 대학의 전통적인 컴퓨터공학 과정에서 다루지 않는 도구 활용 능력을 집중적으로 가르치는 MIT의 실습 중심 강의 - 명령줄, 텍스트 편집기, 버전 관리 시스템 등 개발자가 매일 사용하는 핵심 도구를 효율적으로 다루는 방법을 교육 - 2026년판에서는 AI 기반 개발 도구와 워크플로우를 각 강의에 통합해, 최신 실무 환경에 맞춘 학습 구조를 제공 - 강의는 YouTube 영상으로 공개되어 있으며, OSSU Discord에서 학생과 강사진 간 토론이 가능 - MIT 외부에서도 자유롭게 활용할 수 있도록 오픈소스와 다국어 번역을 지원, 전 세계 개발자 교육에 기여

강의 개요

- 컴퓨터공학 수업이 운영체제나 머신러닝 같은 고급 주제를 다루는 반면, 개발 도구 숙련도는 학생 스스로 익혀야 하는 영역으로 남아 있음 - 본 강의는 명령줄, 강력한 텍스트 편집기, 버전 관리 시스템의 고급 기능 등 실무 필수 기술을 체계적으로 다룸 - 학생들이 학업과 경력 전반에서 수백~수천 시간을 사용하는 도구를 더 효율적이고 매끄럽게 활용하도록 돕는 목적 - 이러한 도구 숙련은 문제 해결 속도를 높이고, 복잡한 문제를 다룰 수 있는 능력을 확장함

AI 통합 학습

- 2026년판에서는 AI 지원 개발 도구와 워크플로우가 소프트웨어 엔지니어링 전반에 확산되고 있음을 반영 - AI의 한계와 적절한 활용법을 인식한 상태에서 사용할 경우, CS 실무자에게 큰 이점을 제공 - 별도의 AI 강의는 없으며, 각 주제별 강의에 최신 AI 도구와 기법을 직접 통합

강의 일정

- 2026년 1월 12일부터 1월 23일까지 총 9회차로 구성 - 주요 주제: Shell 소개, 명령줄 환경, 개발 환경 및 도구, 디버깅과 프로파일링, Git 버전 관리, 코드 배포, Agentic Coding, 코드 품질 등 - 모든 강의는 YouTube 재생목록을 통해 시청 가능

참여 및 커뮤니티

- 수강생은 OSSU Discord의 #missing-semester-forum 채널에서 질문과 토론 가능 - 강의는 Anish, Jon, Jose가 공동 진행하며, 문의는 이메일(missing-semester@mit.edu)로 가능

MIT 외부 확산

- 강의 자료는 MIT 외부에도 공개되어 있으며, Hacker News, Lobsters, Reddit, X, Bluesky, Mastodon, LinkedIn 등 다양한 플랫폼에서 논의됨 - 2019, 2020, 2026년판 모두 온라인에서 활발히 공유되어 있음

번역 및 오픈소스

- 아랍어, 중국어, 독일어, 일본어, 한국어 등 15개 이상 언어로 커뮤니티 번역본이 존재 - 번역본은 외부 커뮤니티가 제작했으며, 공식 검수는 거치지 않음 - 새로운 번역본은 GitHub Pull Request를 통해 추가 가능 - 강의 자료는 CC BY-NC-SA 라이선스로 공개되어 있으며, 소스코드는 GitHub에서 확인 가능

감사의 말

- MIT Open Learning과 SIPB의 지원으로 강의 영상 제작 및 IAP 2026 프로그램 운영이 가능했음

시니어 엔지니어로서 배운 것들 (2021)

나는 술에 취해 있고 아마 후회하겠지만, 지난 10년간 엔지니어로서 배운 것들의 취중 랭킹을 적어본다.

한 잔 더 따르며

한 모금

이런, 와인이 다 떨어졌다.

이런, 와인이 다 떨어졌다.

맥주를 찾았다. 계속 가자.

프로그래밍 언어에 대해

동료에 대해

재택근무에 대해

기술에 대해

데이터 엔지니어링에 대해

삶에 대해

기타

지금 느끼는 감정들

만취 상태이니 제가 한 말은 무시하세요. 장광설 죄송합니다.


스타트업에서 데이터 팀 만들기 (erikbern.com)

7월 1일 : 아침

7월 1일 : 오후

ㅤ(스프레드 시트를 보니 이런 것들이 있다) ㅤ"고객이 티켓을 발행하고 1시간 내에 해결된 고객과, 1시간 이후에 해결된 고객의 전환율 비교를 주문 금액 $100 달러 간격으로 분류하기"

ㅤ(모델에 대해 물어보니) ㅤ- 수많은 VLOOKUP 들로 구성된 구글시트에 올바른 형식으로 알맞는 탭에 복사 해야하는 것 같음 ㅤ- 데이터는 매일 업데이트 되고, 모델의 출력에 따라 팀의 그날 우선순위가 결정 ㅤ- 공급업체(벤더)들에게 나가는 비용도 스프레드 시트로 계산하고 있음

(집에 가서 위스키 한잔을 가득 따른다.. )

[ 무슨 일이 있었던 걸까 ?]

(와 이건 우울하다. 이 문제를 해결하기 위해선 어떤 일을 해야할까)

7월 8일

9월 1일 : 아침

9월 1일 : 오후

[ 무슨 일이 일어나고 있을까 ? ]

9월 2일

1월 3일

[ 무슨 일이 일어나고 있을까 ? ]

4월 1일

[ OK, 무슨 일이 일어나고 있는건가요 ? ]

7월 1일

(집에 가서 샴페인을 터뜨림)

[ 무슨 일이 일어난거지 ?]

(축하합니다. 당신은 샴페인을 들 자격이 있어요)