# farol: 머지 전에 PR 위험 확인하기
# farol: checking the risk of a pull request before the merge

- Site: https://select1.dev/2026-10-26 (English), https://select1.dev/ko/2026-10-26 (Korean)
- Entry: 016
- Published: 2026-10-26 (KST). Launch date: 2026-10-25
- Pick: farol <https://github.com/winnin/farol> (source: Show HN)
- Verified: ran
- What: farol 은 풀 리퀘스트에서 변경된 코드를 사용하는 페이지와 엔드포인트를 정적 분석으로 찾고, 머지 위험 점수와 먼저 읽을 파일을 보여 주는 프로덕트다.
- What (en): farol is a product that uses static analysis to find which pages and endpoints use the code a pull request changes. It shows a merge risk score and the files to read first.
- Why: 오늘의 픽으로 선정한 이유는 AI 리뷰를 거친 뒤에도 머지 승인자가 여러 페이지에 영향을 주는 변경을 알아챌 수 있게 하기 때문이다.
- Why (en): It is today’s pick because it lets the person who approves the merge notice a change that affects many pages, even after an AI review.
- Verdict: 작동한다. 코드를 실행하지 않고 위험도가 높은 파일과 파라미터 타입 변경을 찾았다.
- Verdict (en): It works. Without running the code, it found the high-risk files and a changed parameter type.
- Tags: code-review, static-analysis, pull-request, fastapi, typescript, ai-built

## Checked against the README

- [pass] 같은 풀 리퀘스트에는 항상 같은 결과가 나온다(the same pull request always gets the same answer) — analyze 를 두 번 실행해 JSON 을 비교했다. 분석 시각(analyzedAt)과 단계별 소요 시간(timingsMs)만 달랐다
- [pass] analyze 는 분석하는 저장소의 코드를 실행하지 않고 네트워크를 사용하지 않는다(analyze never executes code of the analyzed repository and never uses the network) — import 되면 파일을 만드는 코드를 app/db.py 첫 줄에 넣었다. analyze 뒤에도 그 파일은 없었다. unshare -n 으로 네트워크를 차단해도 analyze 가 완료됐다
- [fail] farol 이 페이지나 엔드포인트를 분석하지 못하면 그 풀 리퀘스트를 unknown 으로 판정한다(When an entry point cannot be read, the PR is unknown) — 경로를 함수 path("summary") 로 계산하는 라우트를 기존 GET /reports/summary 앞에 추가했다. farol 은 isolated, fast track, 2/100 으로 판정했다. farol 은 분석하지 못한 라우트를 diagnostics.dynamicRoutes 에 나열하는데, 이 목록도 비어 있었다. FastAPI TestClient 로 GET /reports/summary 를 요청하자 새 라우트가 응답했다({"count": 0}, 기준 브랜치는 {"count": 100})

## Measured

- Python 파일 5개와 farol.config.json 으로 된 저장소에서 네트워크를 차단하고 analyze 를 다섯 번 실행했을 때 실제 경과 시간의 중앙값: 0.55초 (date +%s.%N 으로 측정. 0.51, 0.53, 0.55, 0.55, 0.63초)

## 한국어

## 무엇이 문제인가

AI 에이전트가 작성하고 AI 리뷰어가 검토한 풀 리퀘스트도 머지 승인은 개발자가 직접 한다. farol 의 제작자 Erich Oliveira 는 README 에서, 승인자가 모든 풀 리퀘스트를 같은 시간 동안 읽으면 결국 대충 보게 된다고 했다. 그러면 여러 페이지가 함께 사용하는 코드에서 한 줄이 바뀌어 페이지 40개가 망가져도 승인자가 알아채지 못한다고 했다. 제작자는 README 에서 특정 프로덕트를 꼽지 않고 AI 리뷰 에이전트 전반과 farol 을 비교했다. 이 기록에서는 AI 리뷰의 예로 GitHub 의 Copilot 코드 리뷰와 비교한다.

GitHub 의 Copilot 코드 리뷰 문서에 따르면 Copilot 은 풀 리퀘스트에서 문제를 찾아 수정을 제안한다. Copilot 이 풀 리퀘스트에 남기는 요약 댓글에는, 리뷰 뒤에 이 풀 리퀘스트를 승인해도 된다고 판단했는지가 들어간다. GitHub 은 같은 문서에서 Copilot 이 모든 문제를 찾는다는 보장은 없다며, 개발자가 직접 리뷰해 보완하라고 권한다.

## 어떻게 해결했나

farol 은 2026년 10월 25일 Hacker News 에 Show HN 글로 올라왔다. farol 은 AI 리뷰가 끝난 뒤, 머지 승인자에게 이 풀 리퀘스트가 얼마나 위험한지와 어느 파일부터 읽을지를 보여 준다. 기준 브랜치와 풀 리퀘스트 브랜치의 코드를 모두 읽고, 변경된 함수를 호출하는 페이지와 엔드포인트를 찾는다.

이 결과로 풀 리퀘스트를 분류한다. 새 코드만 추가했으면 isolated, 기존 코드를 바꿨으면 touches-existing, 프로젝트 전체에 영향을 주면 global, farol 이 코드 일부를 읽지 못했으면 unknown 이다. 여기에 더해 0부터 100까지의 머지 위험 점수를 계산하고, 리뷰 수준을 fast track, quick review, careful review 가운데 하나로 판정한다. 결과는 풀 리퀘스트 댓글과 HTML 보고서로 보여 준다. farol 은 머지를 막거나 승인하지 않는다.

## 무엇이 다른가

첫째, 판단의 근거가 다르다. GitHub 문서에 따르면 Copilot 코드 리뷰는 여러 모델과 프롬프트를 조합해 작동한다. farol 은 LLM 대신 정적 분석으로 판단한다. TypeScript 와 Go 코드는 타입 검사기로, Python 코드는 표준 라이브러리의 AST 파서로 읽는다. 그래서 결과가 결정적이다. 판정마다 변경된 파일에서 엔드포인트까지 이어지는 호출 경로도 함께 보여 준다. 트레이드오프는 함수로 계산하는 라우트 경로처럼, 코드를 실행해야 알 수 있는 값은 읽지 못한다는 점이다.

둘째, 알려 주는 것이 다르다. farol 은 Copilot 과 달리 코드의 버그를 찾지 않는다. 코드 변경이 어디까지 영향을 주는지, 머지 승인자가 어느 파일을 먼저 읽고 어느 엔드포인트를 직접 시험할지를 보여 준다. 엔드포인트가 받는 파라미터의 타입이 바뀌면 호환성을 깨는 변경으로 표시한다. 제작자는 README 에서 AI 리뷰와 farol 을 함께 사용하되, AI 리뷰 다음에 farol 을 실행하라고 했다.

셋째, 비용이 다르다. GitHub 문서에 따르면 Copilot 리뷰는 한 번에 약 $0.05~$5 의 AI 크레딧을 사용한다. farol 은 MIT 라이선스의 무료 CLI 이고, 로컬이나 CI 에서 실행된다. 대신 Copilot 처럼 클릭 몇 번으로 수정을 적용하는 기능은 없다.

## 어떻게 만들었나

- 공개 저장소의 커밋 이력은 2026년 10월 21일 첫 커밋 "farol 0.1.0" 에서 시작한다. 이 커밋에 파일 504개가 들어 있어, 공개 전의 개발 과정은 저장소에서 확인할 수 없다. 10월 24일까지 커밋은 9개다.
- 커밋 9개에는 모두 Claude 와 함께 작성했다는 Co-authored-by 트레일러가 있다.
- src 디렉토리의 TypeScript 코드는 13,300줄이다. test 디렉토리에는 파일 333개가 있다.
- 제작자는 HN 댓글에서 자신이 Winnin 이라는 회사에서 farol 을 만들었다고 밝혔다. Winnin 이 farol 을 MIT 라이선스로 공개했다고도 했다. UI 디자인을 AI 로 만든 것 같다는 댓글에는, AI 로 만들었고 자신은 디자인 실력이 부족하다고 답했다.

## 실행해 보니

Python 파일 5개로 FastAPI 서비스를 작성했다. 아래 단계마다 브랜치를 만들어 기준 브랜치와 비교해 분석했다.

1. 설치와 설정. npm 으로 farol 0.1.3 을 설치했다. 의존 패키지는 없었다. `farol.config.json` 에 언어와 분석 대상을 지정했다.
2. 기존 코드 변경. `fetch_items` 에 필터를 추가하고, `GET /items/{item_id}` 의 `item_id` 타입을 int 에서 str 로 바꿨다. 판정은 careful review, 점수는 75/100 이었다. `fetch_items` 가 있는 `db.py` 는 엔드포인트 3개 가운데 2개가 사용해, 파일 위험도 세 단계(high, medium, low) 가운데 high 로 분류됐다. `item_id` 의 타입 변경은 호환성을 깨는 변경으로 표시됐다.
3. 계산한 경로. README 에 따르면 farol 이 엔드포인트를 읽지 못하면 풀 리퀘스트는 unknown 이 된다. FastAPI 는 경로가 겹치면 먼저 등록된 라우트가 응답하므로, 경로를 함수 `path("summary")` 로 계산하는 라우트를 기존 `GET /reports/summary` 앞에 추가했다. farol 은 이 라우트를 `GET /reports{path('summary')}` 라는 새 엔드포인트로 보고 isolated, fast track, 2/100 으로 판정했다. 진단 항목의 읽지 못한 라우트 목록도 비어 있었다. FastAPI 의 테스트 클라이언트로 `GET /reports/summary` 를 요청하자 기존 핸들러 대신 새 라우트가 응답했다.
4. prefix 계산 방식 변경. 라우터의 prefix 를 문자열로 지정하는 대신, 기존 prefix 와 같은 값을 반환하는 함수로 계산하게 바꿨다. farol 은 기존 엔드포인트가 삭제되고 새 엔드포인트가 추가됐다고 인식하고 touches-existing, careful review, 42/100 으로 판정했다. 경로는 바뀌지 않았으므로 과한 판정이었지만, 3단계와 달리 위험을 낮게 평가하지는 않았다.
5. 속도. 네트워크를 차단하고 분석을 다섯 번 실행했다. 실제 경과 시간의 중앙값은 0.55초였다.

README 에 따르면 farol 은 같은 풀 리퀘스트에는 늘 같은 결과를 출력하고, 분석 대상 코드를 실행하지 않는다. 이 두 설명도 실행 결과와 일치했다. 같은 분석을 두 번 실행한 결과는 분석 시각과 단계별 소요 시간만 달랐다. `db.py` 첫 줄에 import 되면 파일을 만드는 코드를 넣었지만, 분석 뒤에도 그 파일은 생기지 않았다.

```
"detail": "new entry point GET /reports{path('summary')} (registered in app/reports.py)"
- **Merge risk score:** 2/100 (low; higher is riskier)
{'count': 0}
```

## 확인하지 못한 것

- 큰 저장소의 속도. README 에 따르면 파일 300개인 Python 서비스의 분석 시간은 약 3초다. 실제 서비스 저장소에서 측정하자.
- TypeScript 와 Go 프로젝트. Next.js 앱에서 공유 컴포넌트를 고친 브랜치를 분석해 영향을 받는 페이지 목록을 확인하자.
- 풀 리퀘스트 댓글. `farol comment` 는 GITHUB_TOKEN 이 필요하다. 테스트 저장소의 풀 리퀘스트에 새 커밋을 푸시했을 때 farol 이 앞서 남긴 댓글이 수정되는지 확인하자.

## 판정

작동한다. 코드를 실행하지 않고 위험도가 높은 파일과 파라미터 타입 변경을 찾았다. 다만 함수로 계산한 라우트 경로는 읽지 못했고, 그 라우트가 기존 응답을 바꿨는데도 fast track 으로 판정했다. AI 리뷰를 거친 풀 리퀘스트를 개발자가 직접 승인하고, 라우트 경로를 문자열로 지정하는 경우에 써 볼 만하다. 라우트 경로를 함수로 계산하는 코드가 많은 저장소라면, 오늘처럼 기존 응답이 바뀌어도 가장 가벼운 리뷰 수준인 fast track 으로 판정될 수 있어 이 프로덕트는 적합하지 않다.

### 용어

- Show HN: 개발자 커뮤니티 Hacker News 에서 자신이 만든 프로덕트를 소개하는 글. 제목이 "Show HN:"으로 시작한다.
- LLM: 대규모 언어 모델. 많은 글로 학습해 글을 이해하고 생성하는 AI 모델이다.
- 정적 분석: 프로그램을 실행하지 않고 소스 코드만 읽어 구조와 오류를 찾는 방법.
- AST: 소스 코드를 문법 구조에 따라 트리로 나타낸 자료 구조. 컴파일러와 분석 도구가 코드를 읽을 때 만든다.
- 결정적: 같은 입력을 넣으면 항상 같은 결과가 나오는 성질. 실행할 때마다 결과가 달라질 수 있는 LLM 의 출력과 대비된다.
- 호환성을 깨는 변경: 이전 버전의 API 에 맞춰 만든 클라이언트가 더는 작동하지 않을 수 있는 API 변경.
- CI: 지속적 통합(continuous integration). 코드를 올릴 때마다 빌드와 테스트를 자동으로 실행하는 체계다.

## English

## The problem

Even when an AI agent writes a pull request and an AI reviewer checks it, a person still approves the merge. Erich Oliveira, the maker of farol, says in the README that a person who spends the same time on every pull request ends up skimming. He says they then miss a one-line change to shared code, and that change breaks forty pages. In the README he compares farol with AI review agents in general. This entry uses GitHub’s Copilot code review as the example of an AI review.

According to GitHub’s docs for Copilot code review, Copilot finds issues in a pull request and suggests fixes. After the review, Copilot leaves an overview comment that says whether Copilot judged the pull request ready to approve. In the same docs, GitHub says Copilot is not guaranteed to spot all problems and recommends a human review as well.

## How it solves it

farol was posted as a Show HN on Hacker News on October 25, 2026. It runs after the AI review. It shows the person who approves the merge how risky the pull request is and which files to read first. It reads the code on both the base branch and the pull request branch. Then it finds which pages and endpoints call the changed functions.

A pull request that only adds new code gets the label isolated. One that changes existing code gets touches-existing. One that affects the whole project gets global. If farol cannot read part of the code, it classifies the change as unknown. It also computes a merge risk score from 0 to 100 and picks a review level: fast track, quick review or careful review. The results go into a pull request comment and an HTML report. farol never blocks or approves a merge.

## What is different

The first difference is what the judgment rests on. According to GitHub’s docs, Copilot code review uses a mix of models and prompts. farol uses static analysis instead of an LLM. It reads TypeScript and Go with their type checkers, and Python with the AST parser in the standard library. So its results are deterministic. Each finding also shows the call chain from the changed file to the endpoint. The tradeoff is that it cannot read values that are known only when the code runs, such as a route path computed by a function.

The second difference is what each tool tells you. Unlike Copilot, farol does not look for bugs in the code. It shows how far the change reaches, which files to read first and which endpoints to test by hand. When the type of an endpoint parameter changes, it marks a breaking change. The maker’s README recommends running an AI review first and farol after it.

The third difference is cost. According to GitHub’s docs, one Copilot review uses about $0.05–$5 of AI credits. farol is a free CLI under the MIT license, and it runs locally or in CI. But it cannot apply a fix in a few clicks the way Copilot can.

## How it was built

- The public commit history starts on October 21, 2026, with a first commit named "farol 0.1.0". That commit adds 504 files, so the development before the release is not visible in the repository. There are 9 commits up to October 24.
- All 9 commits have a Co-authored-by trailer for Claude.
- The src directory has 13,300 lines of TypeScript. The test directory has 333 files.
- In an HN comment, the maker said he built farol at a company called Winnin, which open-sourced it. A commenter said the UI design looked like it was made by AI. The maker replied that he did make it with AI and that he lacks design skills.

## Running it

I wrote a FastAPI service with 5 Python files. For each step below, I made a branch and analyzed it against the base branch.

1. Installation and setup. Installing farol 0.1.3 from npm added only the farol package. In `farol.config.json` I set the language and the kind of project.
2. A change to existing code. I added a filter to the list function `fetch_items`. I also changed the type of `item_id` in `GET /items/{item_id}` from int to str. The verdict was careful review, with a merge risk score of 75/100. `db.py` holds `fetch_items`, and 2 of the 3 endpoints use it. farol rated this file high risk, on a scale of high, medium and low. The type change of `item_id` was marked as a breaking change.
3. A computed path. The README says farol classifies the change as unknown when it cannot read an entry point. When paths overlap, FastAPI sends the request to the route registered first. So I added a route before the existing `GET /reports/summary`. Its path comes from the function call `path("summary")`. farol read it as a new endpoint, `GET /reports{path('summary')}`, and classified the change as isolated. It gave a fast track verdict and a score of 2/100. The list of unreadable routes in the diagnostics was empty. I sent `GET /reports/summary` with FastAPI’s test client, and the new route answered instead of the old one.
4. A computed prefix. I changed the router’s prefix from a string to a function that returns the same prefix. farol read this as if an existing endpoint was removed and a new one was added. It classified the change as touches-existing. The verdict was careful review, with a score of 42/100. The paths did not change, so this verdict was too strict. But unlike in step 3, it did not understate the risk.
5. Speed. With the network blocked, I ran the analysis five times. The median wall-clock time was 0.55 seconds.

Two other claims from the README held up: the same pull request gets the same answer, and the analysis never runs the analyzed code. Two runs of the same analysis differed only in the analysis time and the step timings. I made the first line of `db.py` create a file on import. The file did not appear after the analysis.

```
"detail": "new entry point GET /reports{path('summary')} (registered in app/reports.py)"
- **Merge risk score:** 2/100 (low; higher is riskier)
{'count': 0}
```

## Not checked

- Speed on a large repository. According to the README, a 300-file Python service takes about 3 seconds. Measure it on a real service repository.
- TypeScript and Go projects. Analyze a Next.js branch that changes a shared component, and check the list of pages it reaches.
- Pull request comments. `farol comment` needs GITHUB_TOKEN. Push a new commit to a pull request in a test repository, and check that farol edits the comment it left before.

## Verdict

It works. Without running the code, it found the high-risk files and a changed parameter type. But it could not read a route path computed by a function. That route changed an existing response, and farol still gave fast track. It is worth trying if you approve pull requests after an AI review and your service sets its route paths as plain strings. In a repository that builds many route paths with functions, a change to an existing response can still get fast track, the lightest review level, as it did today. This product is not a good fit there.

### Terms

- Show HN: A post on Hacker News, a developer community, in which someone presents what they made. Its title starts with "Show HN:".
- static analysis: Finding the structure and errors of a program by reading its source code, without running it.
- LLM: A large language model. It is an AI model trained on large amounts of text to understand and generate text.
- AST: Abstract syntax tree: a tree that shows the structure of source code. Compilers and analysis tools build it to read code.
- deterministic: Always giving the same result for the same input. LLM output, in contrast, can differ from run to run.
- breaking change: A change to an API that can make clients built for the old version fail.
- CI: Continuous integration: a setup that builds and tests code automatically every time it is pushed.

## Launches the same day

- Steal My Bitcoin <https://stealmybitcoin.com/> (Show HN, 571): 해킹 도전 과제. 최근 알려진 PHP 원격 코드 실행 취약점을 실제로 악용할 수 있는지, 서버의 비트코인을 걸고 시험한다.
- farol <https://github.com/winnin/farol> (Show HN, 58): 코드가 변경되면, 그 코드를 사용하는 엔드포인트와 머지 위험 점수를 보여 준다.
- Hurston-1 <https://huggingface.co/destined/hurston-1> (Show HN, 18): 음성 합성 모델. 미국 남부 억양과 아프리카계 영어 억양을 잘 읽는다고 소개한다.
- Argus <https://github.com/thomasunise/argus> (Show HN, 17): 오픈소스 보안 플랫폼. AI 에이전트를 이용한 공격을 막는다고 소개한다.
- Orisuji <https://apps.apple.com/us/app/orisuji/id6817092898> (Show HN, 11): 퍼즐 게임. 접는 iPhone 을 접었다 펴면서 미로를 푼다.
- gdirect <https://github.com/mizorewww/gdirect> (GitHub, 543): Google Drive 다운로더. Rust 로 작성했고, 버전을 고정한 libaria2 로 파일을 직접 내려받는다.
- ex-ussr-kinship-research <https://github.com/DenisSergeevitch/ex-ussr-kinship-research> (GitHub, 497): 가계 조사 에이전트 스킬. 옛 소련 지역의 교회, 군, 강제 이주 기록에서 가족을 찾는다고 소개한다.
- Raven <https://github.com/mirkobrombin/raven> (GitHub, 373): Windows 앱 실행 도구. 가상 머신 없이 리눅스에서 Windows 설치 프로그램과 앱을 실행한다고 소개한다.
- Moli by Lexmount <https://www.producthunt.com/products/lexmount-browser> (Product Hunt, 221): 헤드리스 브라우저. 에이전트가 사용하는 브라우저 가운데 가장 빠르다고 소개한다.
- AgentGrid.io <https://www.producthunt.com/products/agentgrid-io> (Product Hunt, 162): 공유 작업 공간. 팀원과 에이전트가 같은 공간에서 일한다고 소개한다.
- Badgey <https://www.producthunt.com/products/badgey> (Product Hunt, 159): 이름표 앱. iPhone 화면을 주위에서 보이는 이름표로 만든다.

## Getting started

- Steps: npm install @winninjs/farol@0.1.3 ; node_modules/.bin/farol analyze --base main --head feature --out farol.json && node_modules/.bin/farol html --in farol.json --out farol.html
- Needs: Node.js 22 이상, git, Python 프로젝트는 python3 3.11 이상, Go 프로젝트는 go, 저장소 루트의 farol.config.json. Python 서비스라면 { "language": "python", "surface": "backend" }
- Cost: MIT, 무료. README 에 따르면 계정, 호스팅 서비스, 유료 요금제가 없다.
- Note: 커버리지 보고서를 지정하지 않으면 farol 은 변경된 라인이 모두 테스트되지 않았다고 간주하고 머지 위험 점수를 계산한다.
