Evolving programming languages in the AI era
AI 시대의 프로그래밍 언어는 어떻게 진화할까요
코딩 에이전트가 코드 대부분을 작성하는 시대에도 프로그래밍 언어와 컴파일러는 필요하며, 언어 설계의 초점은 사람의 입력 편의성보다 강한 보장과 도구 연동으로 옮겨갈 수 있다고 주장합니다. 저자는 LSP 대신 질의 가능한 프로그램 데이터베이스를, 디버거 대신 에이전트가 탐색하는 런타임 관측 도구를 제안합니다.
- 주제
AI 요약
Dashbit의 José Valim은 코딩 에이전트가 코드 작성을 맡는 상황에서 프로그래밍 언어와 생태계가 어떻게 달라질지 살펴봅니다. 글은 언어 커뮤니티와 문법의 변화를 돌아보는 성찰, 에이전트가 사용할 개발 도구를 제안하는 구체적인 논의로 나뉩니다.
언어 생태계와 사람 중심 문법
언어마다 공유하는 가치가 있습니다. Python은 명확한 해결책을, Ruby는 프로그래머의 만족을, Lisp는 언어 자체를 바꾸는 능력을 중시해 왔습니다. 에이전트가 코드 대부분을 작성하면 언어 커뮤니티의 소속감이 무엇에 기대야 할지 달라질 수 있습니다.
에이전트는 알고리즘 구현이나 논문 아이디어의 코드화, 언어 간 포팅에 드는 시간과 노력을 줄입니다. 그 결과 규모가 작은 언어 생태계도 큰 생태계를 빠르게 따라잡을 수 있습니다. 반면 필요한 라이브러리를 각자 에이전트에게 만들게 되면, 공동으로 해결책을 만들 동기도 약해질 수 있습니다. 생태계를 만드는 비용은 낮아지지만, 사람들이 함께 모이는 힘도 줄어드는 긴장이 생깁니다.
사람이 코드를 직접 쓰지 않는다면 문법의 편의성도 덜 중요해질 수 있습니다. 선택적 체이닝은 사람이 여러 번의 null 검사를 쓰는 수고를 줄여주지만 에이전트는 반복적인 코드를 부담스러워하지 않습니다. 저자는 토큰 효율도 언어 설계의 우선순위가 아니라고 봅니다. 모델 비용은 낮아지고 컨텍스트 창은 커지고 있기 때문입니다. 따라서 오늘날 모델의 한계에 맞춰 문법만 새로 만든 언어는 오래가는 설계가 되기 어렵다고 주장합니다.
컴파일러와 더 강한 보장
에이전트가 기계어를 직접 쓰면 프로그래밍 언어가 필요 없어질 것이라는 전망도 반박합니다. 데스크톱 애플리케이션은 여러 아키텍처를 지원해야 하므로, 아키텍처와 무관한 표현과 이를 대상 기계에 맞게 낮추는 과정이 필요합니다. 사실상 컴파일러와 고수준 언어를 다시 만드는 셈입니다. 동시성, 분산 시스템, 쿼리, 하드웨어 기술 등 서로 다른 문제에는 서로 다른 추상화와 의미, 보장이 필요하므로 하나의 저수준 언어로 모두 대체하기도 어렵습니다.
사람의 작성 편의를 덜 고려한다면 언어는 표현력과 보장 사이의 균형을 다시 잡을 수 있습니다. 함수 서명을 추론하는 기능은 사람이 직접 적는 수고를 덜어주지만, 에이전트는 명시적인 타입과 의도를 적는 일을 꺼리지 않습니다. 명시성은 컴파일러와 다른 에이전트가 활용할 정보도 늘립니다. 타입을 완전히 추론할 수 있는 언어는 타입 검사가 가능한 언어보다 범위가 좁을 수 있으므로, 추론 편의에 맞추면 표현력과 보장이 제한될 수 있다는 설명입니다.
보장은 정적 검사만으로 만들 필요가 없습니다. 언어가 잘못된 상태를 표현하기 어렵게 만들거나, 타입·증명·정적 분석으로 실행 전에 속성을 확인할 수 있습니다. 메모리 관리와 프로세스 격리처럼 런타임이 보장하는 방식도 있고, 테스트·속성 기반 테스트·퍼징으로 동작을 검증할 수도 있습니다. Erlang과 Elixir는 격리된 프로세스와 메시지 전달로 동시성 구조를 제한하며 장애 격리와 내결함성을 얻습니다. 저자는 언어와 프레임워크가 이런 방법을 조합해 소프트웨어의 보장을 강화해야 한다고 제안합니다.
LSP 대신 프로그램 데이터베이스
Language Server Protocol(LSP)은 IDE에서 사람이 파일·줄·열을 지정하며 코드를 살펴보는 방식에 맞춰 설계됐습니다. 에이전트는 정확한 위치를 기억하지 않으므로 “foo_bar의 문서는 어디 있나요?”처럼 심볼을 바로 묻는 CLI나 도구가 더 적합하다고 저자는 설명합니다. 언어 서버는 심볼, 참조, 호출 그래프, 타입 정보, 데이터 흐름 정보를 이미 만들거나 접근할 수 있습니다. 이를 SQLite, Datalog, 전용 DSL 같은 질의 언어를 갖춘 프로그램 데이터베이스로 공개하자는 제안입니다.
에이전트는 “이 함수를 호출하는 공개 함수 중 특정 조건을 만족하는 항목”이나 “값이 nil이 될 수 있는 경로”처럼 IDE 기능 하나로 제공하기 어려운 질문도 질의로 조합할 수 있습니다. 이런 데이터베이스는 원치 않는 코딩 패턴을 막는 린터로도 쓰일 수 있습니다. 반면 monkey-patching, 암묵적인 훅, 동적 재바인딩처럼 한곳의 코드가 멀리 떨어진 동작을 바꾸는 구조는 분석을 어렵게 만듭니다. 따라서 코드의 지역성은 여전히 중요합니다.
디버거 대신 런타임 관측
사람은 브레이크포인트를 설정하고 한 줄씩 실행하며 변수를 확인합니다. 에이전트는 코드를 계측하고 실행 추적을 수집해 정보를 더 빠르게 비교할 수 있으므로, 런타임과 상태를 프로그램으로 탐색하는 인터페이스가 필요하다고 제안합니다. 에이전트가 개발 수명주기를 맡는다면 로그와 대시보드에만 기대지 않고 운영 중인 시스템을 진단하고 감시할 수도 있습니다.
Erlang VM은 프로세스, 소켓, 애플리케이션, 감독자, ETS 테이블, 메시지 큐 등을 검사하는 기능을 런타임에 제공합니다. 저자는 남은 과제를 이 기능을 에이전트에 안전하게 공개하는 일로 봅니다. 도구 모음이나 질의 언어, 샌드박스를 활용할 수 있습니다.
Hacker News 반응
- @imtringued — 에이전트는 언어별 생태계를 약화시키는 동시에 언어에 종속되지 않는 생태계를 강화합니다. 좋아하는 언어와 생태계를 고르면, AI로 그 생태계를 다른 언어에 포팅할 수 있습니다. 소프트웨어를 다른 언어로 옮기는 일이 의미 없어지도록 모든 언어에서 동시에 생태계를 구축해야만 보호할 수 있습니다.
- @pjm331 — 제 해석은 조금 다릅니다. AI와 각자 따로 일하게 되면서 사람끼리 협력할 필요가 줄어들고, 그 결과 사람 중심의 생태계가 약해진다는 뜻으로 읽었습니다. 언어에 종속되지 않는 생태계의 구체적인 예가 있나요? 어떤 모습일지 잘 떠오르지 않습니다.
- @andriy_koval — 생태계를 포팅하는 일이 그렇게 간단하다고 생각하지 않습니다. 지금까지 본 포팅 사례에는 자체 완결성과 높은 테스트 커버리지가 필요했습니다. 그래야 AI가 수백만 번 반복하며 새 구현의 버그를 고칠 수 있습니다. 그렇지 않으면 버그가 많고 유지보수하기 어려운 결과가 나올 수 있습니다.
- @jacquesm — 에이전트 코딩에 가장 적합한 언어가 사람이 쓰는 언어라고 왜 가정하나요? AST를 직접 다루거나 사람이 쓰기에는 어렵지만 LLM에는 잘 맞는 다른 형태의 프로그래밍을 쓸 수도 있습니다.
- @kloop — 사람의 코드 검토가 큰 병목입니다. 병목이 아닌 부분을 최적화해도 도움이 되지 않습니다.
- @jerf — 에이전트가 코드를 어떻게 만들었는지는 감사하기 어려워도, 사람이 읽을 수 있는 코드를 내놓는 능력은 이미 충분합니다. 에이전트가 조금 더 효율적일지조차 확실하지 않은데 사람이 읽지 못하는 언어를 위해 그 장점을 포기하는 건 어리석습니다. 사람이 읽을 수 없는 AI 전용 언어의 개발을 금지하는 데 찬성하겠습니다. 정말 잘못된 방향입니다. 물론 결국 그렇게 되겠지만요.
- @spankalee — 언어가 잘못된 상태를 표현하기 어렵게 하고, 타입·증명·정적 분석과 런타임 격리, 테스트와 퍼징으로 확인하는 설계가 제가 Zena를 만드는 이유와 맞닿아 있습니다. AI는 엄격한 언어도 다룰 수 있습니다. Zena에는 정적으로 검증하는 구조적 동시성, 단위, 계약, 더 많은 형식 기법을 넣을 계획입니다. WebAssembly를 이용한 세밀한 격리도 생성 코드의 권한과 버그, 취약점의 영향을 제한하는 데 중요합니다. 코드 대부분을 생성하더라도 사람이 읽기 쉽고 의미가 단순한 언어에 가치가 있다고 기대합니다.
- @demibabs — 제 생각에는 에이전트용 프로그래밍 언어라는 발상 자체가 잘못된 것 같습니다. 학습 사례가 부족하니 에이전트가 자연히 잘 다루지 못할 겁니다.
- @spankalee — Zena를 만들며 겪은 바로는 그렇지 않습니다. Opus, Fable, Gemini Flash와 Pro는 컨텍스트에 조금만 들어가면 문법 오류를 거의 내지 않고, 그런 오류도 초기에 잡힙니다. 가끔 새 기능을 충분히 활용하지 않는 문제가 있지만 코드베이스에서 그 기능을 많이 쓰지 않기 때문이기도 합니다. 더 나은 패턴을 권하도록 스킬과 린터 제안을 만들고 있습니다.
- @talon8635 — 더는 아무도 코드를 읽지 않는다면 AI가 바이너리나 기계어를 작성하게 하면 안 되나요?
- @rspeele — 사람이 코드를 읽지 않더라도 AI는 코드를 ‘이해’하려고 다시 읽습니다. 고수준 개념을 표현하고, 불분명한 점프 대신 알아보기 쉬운 제어 흐름을 쓰고, 이름을 붙이는 일은 AI 코더와 리뷰어에게도 사람에게 그랬던 것처럼 도움이 됩니다.
- @mhalle — 기계어는 코드 한 줄이나 단위당 표현력이 높지 않습니다. 저수준 언어는 고수준 언어보다 LLM의 컨텍스트를 더 많이 차지합니다. 저수준 언어를 효과적으로 쓰려면 LLM이 매번 서브루틴 같은 고수준 구조를 처음부터 만들어야 합니다. 사람이 작은 서브루틴도 이해하기 어려워 어셈블리를 제한적으로만 쓰는 이유와 다르지 않습니다.
- @Animats — LLM은 지역적인 목표를 잘 최적화합니다. 컴파일 때 타입을 맞추기, 진입·종료 조건 검사, 단위 테스트는 모두 그런 목표입니다. 그래서 이런 구조가 AI 생성 코드에 도움이 됩니다. 원하는 출력을 맞추는 전역 목표도 이제는 어느 정도 작동합니다. 누군가 Fable로 GPU에서 기준 구현과 같은 결과를 내는 JPEG 2000 디코더를 생성했다고 전했습니다.
- @rao-v — 에이전트가 복잡한 함수나 클래스를 먼저 쓰고 나중에 테스트를 붙이는 대신, 언어 환경에서 더 많은 명세를 작성하도록 유도하면 좋겠습니다. 클래스에 테스트, 불변 조건, 퍼저 매개변수, 성능 목표와 현실적인 입력, 경합 상태 스트레스 테스트를 함께 넣고 싶습니다. 컴파일러나 린터가 일부 또는 전부를 실행하고, 에이전트가 실행 시간을 조절하도록 하면 좋겠습니다. 이런 항목은 후속 작업이 아니라 처음부터 함께 작성해야 합니다.
원문: Dashbit / 번역·요약: Trawling