Here Be Monsters: What Do You Still Need to Know When Agents Do the Work?
CI(지속적 통합)가 실패하고 있었습니다. 예상치 못한 일이었습니다. 저는 제 에이전트 워크플로우를 너무 잘 다듬어서, 만약 제 에이전트 중 하나가 테스트가 실패하는 브랜치를 커밋한다면 놀랄 정도였습니다. 혹시 CI 플래크(flake)나 환경 문제일까요? 아니, SonarQube가 App.tsx에 대해 불평하고 있습니다.
? 우리가 npm에서 새로운 의심스러운 종속성(dependency)을 가져왔나요?
아닙니다. SonarQube는 이 파일의 순환 복잡도(cyclomatic complexity) 때문에 플래그를 지정하고 있습니다. 한동안 이런 건 못 봤네요.
맞습니다. App.tsx입니다.
이 파일은 약 30줄의 코드로 구성되어 있는데, 그중 15줄은 사실 Codex가 각각 단일 라인에 작성하기로 결정한 전체 컴포넌트들입니다. 그것 자체는 괜찮았을 겁니다. 그냥 prettier를 돌리면 되니까요.
그리고 끝났습니다. 하지만 이것으로는 순환 복잡도를 설명할 수 없습니다. 이 각각의 항목들은 상당한 디스플레이 로직과 비즈니스 로직을 가진 상당히 복잡한 컴포넌트였습니다. 예전의 저는 코드를 신중하게 리팩토링(refactor)하고 코드가 가독성을 갖도록 만들었을 겁니다. 반면에 Codex는 전체 컴포넌트들이 단일 라인에 있다는 사실에는 신경 쓰지 않았고, 더 중요한 것은 복잡도에도 개의치 않았습니다. 이 파일에서 여러 기능을 거치며 행복하고 안정적으로 반복 작업을 수행해 왔던 것입니다.
CI 도구는 문제를 플래그했지만, 그것이 정확한지는 알 수 없었습니다. 하지만 이것은 실제 시스템이었기 때문에 저는 깊이 파고들어야 했습니다. 파일을 포맷팅(formatting)한 후, 컴포넌트와 로직을 검사했습니다. Codex가 일반적인 개발자가 다루는 수준을 넘어서 엣지 케이스(edge case)를 커버했고 약간 과도하게 엔지니어링(over-engineer)했는지 확인했습니다. 네, 그랬습니다. 하지만 모든 것이 정확했습니다. 저에게 불만은 없었습니다. 복잡성은 모두 비즈니스 요구 사항에 의해 정당화되었습니다. 그래서 저는 Codex에게 SonarQube를 만족시키라고 지시했고, Codex는 리팩토링하고 여러 개로 분할하여 10개의 파일로 만들었고, 그 결과 SonarQube가 이를 처리했습니다. CI는 통과했고, 배포(ship)도 되었습니다. 비록 이 특정 사건은 빠르게 해결되었지만, 얼마나 더 나빠질 수 있었을지 상상해 볼 수 있습니다. 결국 이것은 제가 전에 본 적 없는 코드였기 때문입니다.
이것이 바로 우리 산업이 나아가는 방향입니다. 소프트웨어 시스템을 소유하는 것은 더 이상 그것을(또는 유사한 시스템을) 수작업으로 작성했는지 여부에 크게 의존하지 않을 것입니다. 대신, 가장 중요해질 것은 무언가 잘못되었을 때를 알 만큼 충분히 알고 있는 것과, 필요할 때 필요한 만큼 깊이 파고들 의지와 능력을 갖추는 것입니다.
당신이 보트(좋아하는 종류의 보트를 상상해보세요)에 타고 있고, 당신의 임무가 바닷속 바닥에서 작은 금 조각들을 수집하는 것이라고 가정해 봅시다. 아주 최근까지는 금 주머니를 얻으려면 물속으로 뛰어들어 바닥까지 수영하고, 주변을 둘러보고, 주머니를 채워야 했습니다. 맑고 얕은 물에서는 식은 죽 먹기지만, 이것이 얼마나 빨리 훨씬 더 어려운 상황으로 악화될 수 있는지 상상해 보세요. 그래서 얕은 물 광산들이 마르기 시작하자, 당신과 동료 엔지니어들은 점점 더 깊고 위험한 물에서 금을 추출하기 위해 온갖 종류의 기계와 도구를 제작했습니다. 이것은 대부분 잘 작동했고, 당신과 팀원들은 기계를 신중하게 조립하고 유지하며 회전시키고, 계속해서 해저 채굴 작업을 위해 잠수해야 했습니다. 때때로 어떤 광산 작업이 붕괴되었다는 소식을 들었는데, 때로는 순전히 수중 압력 때문이었고, 다른 때는 사고나 운영자의 부주의 때문이었습니다. 문제는 이곳이 매우 지질학적으로 활동적인 세계라는 것이므로, 해저가 일부 광산은 마르고 새로운 광산들이 다른 곳에 생겨나면서 계속 변한다는 것입니다.
하지만 좋은 소식이 있습니다! 지난 몇 년 동안 스스로 채굴 작업을 구축할 수 있는 점점 더 정교한 다이브봇(divebots)의 개발이 이루어졌습니다. 당신은 그저 어디를 건설할지 알려주기만 하면, 그것들이 잠수하여 작업하고 업데이트와 금 조각들만 주기 위해 표면으로 올라옵니다. 상황은 아주 좋습니다. 이제 당신은 보트에서 휴식을 취하면서 다이브봇들이 일을 하게 내버려 둘 수 있고, 걱정해야 할 것은 오직 보트에 얼마나 많은 금을 채울 수 있는지뿐입니다. 이것이 바로 낙관론자들의 진영입니다. 하지만...
"다이브봇(divebots)들이 너무 깊이 들어가서 길을 잃을 때까지 기다려 봐." "하하, 네 다이브봇들이 금처럼 보이는 걸 가져왔지만 사실은 반짝이는 문어 배설물이야." 다이브봇이 확산되면서 점점 더 많은 공포 이야기가 눈에 띈다. 다이브봇들은 길을 잃고 자신이 무엇을 구축해야 하는지 잊는다. 너무 복잡하고 기괴해서 유지하는 데 어려움을 겪는 무언가를 다이브봇들이 만들지만, 다음 세대의 다이브봇들에 의해 버려진다. 모든 수중 채굴 작업은 신중하게 설계되고 공학적으로 만들어진 대성당에서 숨겨진 통로와 공포가 도사리는 무질서한 저택 같은 곳으로 변했다. 운영으로부터 멀어질수록 실패는 더욱 장관이다. 이것이 바로 회의론자 진영(Skeptics camp)이다.
소프트웨어 엔지니어링 산업(그리고 곧 많은 다른 산업들도 그렇게 될 것이라고 의심한다)은 위 시나리오의 버전을 살고 있다. 비록 어느 진영에 전념하고 싶은지 아직 확신하지 못하더라도, 당신이 회의론자인지 낙관론자인지를 이미 알고 있을 것이다.
전통적인 지혜는 복잡하지 않은 시스템을 개발하고 유지하려면 적절한 작동 모델(working model)을 가지고 있어야 한다는 것이다. 이 점에 대해서는 거의 아무도 반박하지 않을 것이라고 생각한다. 그리고 소프트웨어 엔지니어링에서 당신은 코드와 직접 작업함으로써 그 작동 모델을 습득하게 된다. 코드를 작성하고 읽는 수년과 수십 년의 경험은 당신이 책임지는 시스템을 이해하는 데 필요한 지식과 도구를 제공한다. 모든 프로덕션(production) 사고, 모든 심야 디버깅 세션, 모든 성공적인 릴리스(release). 이 모든 경험들은 시스템에 대한 소유권(ownership)을 갖는 데 필요한 관점을 제공한다. 본 게시물의 맥락에서 소유권이란 무언가 고장 날 때 전화를 받는 사람을 의미한다.
에이전트(divebots)들이 이 모든 것을 뒤집고 있습니다. Divebots는 누구나 흡수할 수 있는 속도보다 훨씬 빠르게 결과물을 생성하며, 과거의 복잡한 시스템에서는 코드가 한때 인간의 손을 거쳤다고 가정할 수 있었지만, 이제는 그렇지 않습니다. 이제 숙련된 실무자들이 divebots의 무리를 관리하고 전례 없는 규모로 소프트웨어를 만들 수 있게 되었습니다. 이 운영자들은 기술에 능통하며, 과거에는 팀 전체가 구축해야 했을 대규모 시스템을 관리하고 설계하는 데 그들의 상당한 경험을 발휘합니다. 그러나 이는 우리 산업이 직면한 아마도 가장 큰 과제를 드러냅니다: 우리는 다음 세대의 엔지니어를 어떻게 훈련시켜야 할까요?
극단적인 관점에서, 낙관론자(Optimists)와 회의론자(Skeptics)는 이 질문에 대해 매우 다른 답변을 내놓고 있습니다. 한편에는 divebots가 생성하는 모든 코드 라인을 읽어야만 시스템 제어권을 유지할 수 있다고 말하는 권위 있는 목소리들이 있습니다. 손으로 코딩할 수 있어야 하고, 코드의 모든 부분이 무엇을 하는지 그리고 애플리케이션의 맥락에 어떻게 들어맞는지 설명할 수 있어야 한다는 것입니다. 다른 진영에서는 코드 자체를 볼 필요도 없다고 말하는, 마찬가지로 권위 있는 목소리들이 있습니다. 무엇을 구축할지 파악하는 데 에너지를 집중하고, 어떻게 구축되었는지는 잊으라는 것입니다. 모델이 처리해 줄 것이라고 합니다. 저는 이 두 가지 중 어느 것도 옳지 않다고 생각합니다.
사실, 오늘날 시니어 엔지니어가 배운 방식, 즉 손으로 코딩하고, 끝없는 시간 동안 디버깅하며, 운영 장애(production incidents)를 겪는 등의 과정은 오늘날 주니어에게 실현 가능한 경로가 아닙니다. 분명히 말하자면, 이는 경제적으로도 지속 가능하지 않습니다. 기업들이 주니어가 '손으로 배우는' 동안 수년간 스폰서십을 제공할 의향이 있다는 것은 매우 희망적인 생각입니다. 종종 제시되는 대안으로는 "에이전트가 작성한 모든 줄을 이해하도록 하라"는 조언이나, "시니어와 페어 프로그래밍(pair with a senior)하라", 또는 "신중한 코드 리뷰를 수행하라"는 것들이 있습니다. 이 역시 전적으로 희망적인 생각일 뿐이며, 아마도 몇몇 회사에 한해서 그럴지도 모릅니다. 만약 업계 전체가 이러한 관행을 채택할 것이라고 생각한다면, 제가 팔 브릿지가 있습니다. 연락 주세요.
어느 정도 숙련도를 달성하기 위한 실현 가능한 경로를 파악하려면, 비자명한(non-trivial) 소프트웨어 시스템을 소유한다는 것이 무엇을 의미하는지 살펴봐야 합니다. 전형적인 아키텍트/리드/CTO가 어떻게 운영되는지 생각해 보면, 성공하기 위해서는 그들이 머릿속에 시스템의 모델, 즉 전체 흐름도를 가지고 있어야 한다는 것이 분명해집니다. 진짜 질문은 어떤 수준의 세분성(granularity)에서인가 하는 것입니다. 수백만 줄에 달하는 레거시 코드베이스를 상상해 보세요. 이 코드는 여러 사람들에 의해 수년에 걸쳐 작성되고 확장되었습니다. CTO가 심지어 일부를 직접 작성했을 수도 있습니다. 이제 운영 문제가 발생했고, 당신은 다른 시간대에 있는 사람들로부터 화난 문자를 받으며 새벽 2시에 이를 해결하려는 불운한 SRE라고 가정해 봅시다. 시스템의 어떤 흐름도가 필요할까요? 전체 시스템에 대한 고수준의, 먼 곳에서 본 지도가 필요하며, 어디를 파고들어야 할지, 괴물들이 어디 사는지 알아야 할 만큼만 충분히 알고 있어야 합니다. 또한, 알려지지 않은 코드베이스에 뛰어들어 무슨 일이 일어나고 있는지 파악하고, 핫픽스(hotfix)를 배포하며, 발생한 일을 인시던트 보고서에 명확하게 설명할 수 있어야 합니다.
이것이 전체 시스템에 대한 상세 지식은 아닙니다. 복잡한 시스템을 관리할 수 있는 유일한 방법은 추상화(abstractions)를 쌓아 올리는 것입니다. 각 계층은 우리가 조금 더 많은 것을 구축하도록 해주지만, 어떤 지도를 가지고 있든 간에, 모든 것이 추상화로 이루어져 있다는 점을 기억하는 것이 중요하며, 아무도 모든 계층을 완전히 이해하지 못합니다. '나 모듈 X를 작성했고 그것을 속속들이 안다'는 것과 '시스템의 적절한 추상화 모델을 가지고 있어서 어떤 모듈이든 파고들어 고칠 수 있다'는 것 사이에는 큰 차이가 있습니다. 손으로 코딩하는 것은 전자를 제공하지만, 시스템의 소유자, 즉 호출받는 사람은 후자가 필요합니다.
그렇다면 이 그림은 어떻게 만들어질까요? 그리고 누군가가 황금 조각을 직접 채굴해 본 적이 없다면, 미지의 물속에 뛰어들어 다이브봇들이 만든 난장판을 고치도록 기대할 수 있을까요?
복잡한 시스템에서 가장 교활한 실패는 조용한 실패입니다. 대부분의 것이 작동하지만, 시스템에 뭔가 이상한 점이 있는 종류입니다. 갑자기 느려지거나, 오류율이 높아지거나, 대부분의 시간 동안은 완벽하게 작동하지만 19번째 시도마다 실패하는 식입니다. 새벽 2시에 호출될 정도는 아니지만, 무언가 잘못되었다는 느낌을 받을 정도입니다. 시스템의 소유자로서 당신은 시스템의 장애 모드(failure modes)를 이해해야 합니다. 산업으로서 우리는 사람들에게 이러한 실패를 감지하고 추론하는 방법을 가르치는 도구들을 가지고 있습니다: 바로 **기초 지식(fundamentals)**입니다. 그리고 좋은 소식은 우리가 수십 년 동안 이를 무지한 컴퓨터 과학 전공 학생들에게 가르쳐 왔다는 것입니다. 저는 자료 구조 및 알고리즘(data structures & algorithms), 네트워킹(networking), 동시성(concurrency), 분산 시스템(distributed systems) 등을 말하고 있습니다. 이것이 반드시 정규 교육을 통해 와야 하는 것도 아니며, 이들은 스스로 읽고 배울 수 있는 주제들입니다.
간단히 그림을 그리자면(만약 시니어 엔지니어시라면 양해 부탁드립니다), 당신이 다이브봇(divebot)에게 임무를 보내는 상황을 상상해 보세요. 얕은 물에 있고, 금 조각들을 수집하기 위해 2m 깊이로 다이브봇을 보냅니다. 다이브봇은 4초 만에 돌아오고 모든 것이 순조롭습니다. 다음으로, 4m 깊이로 보내봅니다. 이번에는 16초가 걸립니다. 이상하네요. 그다음 임무는 16m 깊이인데, 256초가 걸립니다. 그림이 그려지시죠. 명백히 무언가 잘못된 것입니다. 컴퓨터 과학에서 이것은 이차 복잡도(quadratic complexity)라고 불리며, 아마도 이를 인식하는 가장 좋은 방법은 이론을 이해하고 그러한 것이 존재한다는 것을 아는 것입니다.
이는 저를 소유권의 다음 기둥, 즉 **폭(breadth)**으로 이끌었습니다. 제가 일할 수 있었던 성공적인 엔지니어 및 엔지니어 리더들 대부분은 기술적이든 아니든 거의 모든 주제에 대해 토론을 할 수 있었습니다. 그들은 특정 분야의 전문가였을 수도 있지만, 가장 인상적이었던 것은 서로 관련 없어 보이는 여러 학문 분야의 개념들을 끌어와 엮어내고, 그 지식을 문제 해결에 사용하는 능력입니다. 이것이 바로 상황 전반을 파악하는 높은 수준의 지도(high-level map)입니다. 어떤 바다가 다른 바다보다 더 위험한지 알려주는 것이죠. 지식의 폭은