Skip to content

Split View: 국내 개발 블로그 명글 큐레이션 2 — 장애 회고와 트러블슈팅, 직접 열어 확인한 12편

✨ Learn with Quiz
|

국내 개발 블로그 명글 큐레이션 2 — 장애 회고와 트러블슈팅, 직접 열어 확인한 12편

한국 개발자가 가장 잘 쓰는 장르

한국어 개발 글을 오래 읽다 보면 한 가지 인상이 남습니다. 개념 정리 글은 영어권과 겹치는 내용이 많은데, 장애 회고는 그렇지 않습니다. 자기 서비스에서 자기가 겪은 일을 쓰기 때문에 대체 불가능하고, 실패를 감추지 않는 글일수록 정보 밀도가 높습니다.

선정 방법은 시리즈 전체와 같습니다. 검색으로 후보를 찾은 뒤 글을 직접 열어 확인했고, 그중에서 설명이 구체적이고 재현 가능한 것을 골랐습니다. 이 글에서는 기준을 하나 더 붙였습니다. 증상과 원인이 다른 곳에 있었던 글, 즉 처음에 의심한 것이 범인이 아니었던 과정이 남아 있는 글을 우선했습니다. 그런 글이 실제 조사에 가장 가깝기 때문입니다.

이 목록은 편집자의 선택이며 순위가 아닙니다. 조회수나 인기는 측정하지 않았고 측정할 수단도 없습니다. 다른 사람이 골랐다면 목록이 달라졌을 것입니다.

출처는 개인 블로그를 우선했습니다. 이 글은 velog 비중이 높은데, 장애 회고라는 장르 자체가 velog에 몰려 있기 때문이지 velog가 더 낫다는 뜻은 아닙니다.

링크는 2026-08-12에 직접 열어 확인했습니다. 개인 블로그 글은 사라지거나 주소가 바뀔 수 있습니다.

원인이 엉뚱한 곳에 있던 사건들

좋은 트러블슈팅 글의 표식은 하나입니다. 처음 의심한 곳과 실제 원인 사이의 거리가 남아 있는가.

사내 서비스 장애 해결 일대기 - p6spy 사용시 DB Routing 안됨, HikariCP 커넥션 누수

  • 블로그 · 작성자: Just Do It · uHan2 (velog)
  • 한 줄 요약: 읽기와 쓰기 데이터소스 라우팅이 조용히 실패한 원인이 SQL 로깅 라이브러리에 있었다는 것을 추적한 기록입니다.
  • 이런 사람에게: 리드 레플리카를 붙였는데 트래픽이 기대만큼 분산되지 않는다고 느낀 적 있는 사람.

이 글이 목록의 첫 자리에 있는 이유는, 원인이 있으리라고 아무도 의심하지 않을 위치에 있었기 때문입니다. SQL을 예쁘게 찍어 주려고 붙인 라이브러리가 커넥션을 감싸면서 지연 획득 프록시를 건너뛰게 만들었고, 그 결과 라우팅이 무력화되었습니다. 개발 편의를 위해 넣은 도구가 런타임 동작을 바꾸는 전형적인 사례이고, 이런 종류는 코드 리뷰로 잡히지 않습니다. 저자가 발견한 내용을 상류에 알린 대목까지 있어서 조사의 마무리가 어떤 모습이어야 하는지도 보여 줍니다.

Timeout이 작동하지 않은 이유

  • 블로그 · 작성자: sejinkim.log · sejin (velog)
  • 한 줄 요약: 타임아웃을 3초로 걸어 두었는데 15초 동안 멈춰 있던 이유가 DNS 조회에 있었다는 것을 밝힙니다.
  • 이런 사람에게: 외부 API 장애가 우리 서비스로 전파되는 경로를 막고 있다고 믿는 사람.

타임아웃을 설정했으니 안전하다고 생각하는 것은 흔한 오해이고, 이 글은 그 오해가 깨지는 지점을 정확히 짚습니다. 이름 해석은 대개 HTTP 클라이언트의 타임아웃 바깥에서 동기적으로 일어나기 때문에, 설정값이 걸리지 않는 구간이 남습니다. 클라이언트 구현에 따라 동작이 갈린다는 점까지 비교해 놓아서, 자기 스택에서 확인할 항목이 분명해집니다. 짧지만 이 글을 읽고 나면 타임아웃 설정을 보는 눈이 달라집니다.

문제 현상 이면에 숨겨진 원인 찾기: TCP 통신 트러블슈팅

  • 블로그 · 작성자: JooSing (velog)
  • 한 줄 요약: 애플리케이션 레벨 예외로 보이던 증상의 실제 원인이 TCP 하프 클로즈였다는 것을 패킷 분석으로 밝혀냅니다.
  • 이런 사람에게: 상태 관리 버그처럼 보이는 문제가 사실은 네트워크 계층에 있을 가능성을 열어 두고 싶은 사람.

제목이 이 글의 요지를 그대로 담고 있습니다. 예외 메시지는 중복 처리 문제를 가리키고 있었지만 실제로는 연결이 절반만 닫힌 상태로 남아 다음 접속을 막고 있었습니다. 애플리케이션 로그만 보고 있었다면 영원히 찾지 못했을 원인이고, 패킷을 떠 본 것이 전환점이 됩니다. 계층을 넘나들며 조사하는 과정이 그대로 남아 있어서, 조사 범위를 언제 넓혀야 하는지에 대한 감각을 얻을 수 있습니다.

쿠버네티스 네트워크 문제 트러블슈팅

  • 블로그 · 작성자: hyeongjun-hub.log · hyeongjun Jo (velog)
  • 한 줄 요약: 운영 클러스터에서 반복되던 지연 스파이크의 원인이 헬름 차트가 막아 놓은 포트 하나에 있었다는 것을 2주에 걸쳐 밝혀낸 기록입니다.
  • 이런 사람에게: 원인 불명의 간헐적 지연 때문에 대시보드만 들여다보고 있는 사람.

이 글의 규모가 인상적입니다. 세 명이 2주 동안 붙었고, 그 과정에서 지웠던 가설들이 남아 있습니다. 최종 원인은 로드 밸런서 구성 요소들이 서로를 인식하는 데 쓰는 포트가 막혀 있었고, 그 결과 여러 노드가 같은 가상 IP를 동시에 광고하면서 ARP 테이블이 흔들린 것이었습니다. 차트 기본값이 만든 문제라는 점에서, 관리형 구성 요소를 쓰는 모든 팀에게 해당하는 이야기입니다. 조사 도구와 순서가 구체적으로 적혀 있어 재현 가능성도 높습니다.

메모리와 CPU를 붙잡고 늘어진 기록

느리다와 죽는다는 다른 문제이고, 조사 도구도 다릅니다. 이 절의 글들은 그 도구를 실제로 씁니다.

Java의 heap dump를 이용한 OOM 원인 분석 with Eclipse MAT

  • 블로그 · 작성자: kyle's devlog (velog)
  • 한 줄 요약: 채점 서버가 죽은 원인을 힙 덤프로 추적하고, 다음 사고를 위해 덤프 수집을 자동화하는 과정까지 다룹니다.
  • 이런 사람에게: OOM이 났을 때 로그 말고 무엇을 봐야 하는지 모르겠는 사람.

원인 자체도 흥미롭습니다. 사용자가 제출한 무한 루프 코드가 거대한 출력 파일을 만들었고, 그것을 통째로 읽어 들이는 코드가 힙을 채웠습니다. 하지만 이 글의 더 큰 가치는 사후 대비입니다. 덤프가 생기면 자동으로 보관 저장소로 올리는 장치를 만들어 두었기 때문에, 다음 번에는 재현을 기다리지 않아도 됩니다. 히스토그램과 도미네이터 트리를 어떤 순서로 보는지도 구체적으로 나와 있어서 도구 사용법 문서로도 씁니다.

3. [400분이 걸리는 10만 개의 알림 요청 시간을 줄여보자] 왜 CPU 스파이크가 발생할까? JVM WarmUP으로 해결해보자

  • 블로그 · 작성자: rawfish의 개발공부.log · 이건회 (velog)
  • 한 줄 요약: 배포 직후에만 CPU가 99퍼센트를 치던 현상을 JIT 컴파일 단계까지 내려가 설명하고 워밍업으로 해결합니다.
  • 이런 사람에게: 배포 직후 몇 분 동안 응답이 느린 것을 어쩔 수 없는 일이라고 받아들이고 있던 사람.

배포 직후의 성능 저하를 캐시가 비어서라고 뭉뚱그리는 경우가 많은데, 이 글은 프로파일이 없는 상태에서 JIT 컴파일러가 하는 일까지 내려갑니다. 스레드 덤프와 모니터링 화면과 부하 테스트 결과가 함께 붙어 있어 주장이 관측으로 뒷받침됩니다. 시리즈의 세 번째 편이라 앞뒤 맥락이 있고, 10만 건 처리라는 구체적인 목표가 있어 개선 폭도 숫자로 나옵니다. 워밍업이 필요한 조건과 필요 없는 조건을 구분하는 데도 도움이 됩니다.

[Java] 에러 스택 트레이스 최적화를 도와주는 -XX:+OmitStackTraceInFastThrow(사라진 예외 스택 트레이스)

  • 블로그 · 작성자: MangKyu's Diary (망나니개발자)
  • 한 줄 요약: 같은 자리에서 반복되는 예외의 스택 트레이스가 어느 순간부터 사라지는 이유를 JVM 최적화 옵션으로 설명합니다.
  • 이런 사람에게: 로그에 예외 이름만 남고 스택 트레이스가 없는 것을 로깅 설정 문제로 의심해 본 사람.

이 현상은 조사하는 사람을 정확히 막아섭니다. 가장 자주 나는 예외일수록 스택 트레이스가 없어지기 때문입니다. 원인이 로깅이 아니라 JVM의 최적화라는 것을 모르면 엉뚱한 곳을 몇 시간 뒤지게 됩니다. 반복 호출로 트레이스가 사라지는 것을 직접 재현하는 예제가 붙어 있어 자기 환경에서 확인해 볼 수 있습니다. 아는 사람에게는 상식이지만 모르면 절대 못 찾는 종류의 지식이고, 그런 지식을 한국어로 정리해 둔 글은 귀합니다.

데이터베이스와 커넥션이 만든 사고들

애플리케이션 코드는 멀쩡한데 데이터 계층에서 무너지는 경우가 많습니다. 원인이 프레임워크의 기본 동작에 있을 때가 특히 그렇습니다.

[트러블슈팅 - DB] 외래키(Foreign Key)와 데드락(DeadLock) 그리고 쿼리 지연 실행

  • 블로그 · 작성자: hocaron.log · Hocaron (velog)
  • 한 줄 요약: 명시적 락을 걸지 않았는데도 발생한 데드락의 원인이 외래키 제약과 쓰기 지연 순서에 있었다는 것을 밝힙니다.
  • 이런 사람에게: 데드락 로그를 봤는데 코드 어디에도 락을 거는 부분이 없어 당황한 적 있는 사람.

이 글은 두 가지 지식을 연결합니다. 자식 행이 바뀌면 부모 행에 잠금이 전파된다는 것과, ORM이 쓰기를 미뤘다가 정해진 순서로 내보낸다는 것입니다. 각각은 알려진 사실이지만 둘이 겹쳤을 때 데드락이 결정적으로 재현된다는 대목이 이 글의 기여입니다. 해결책으로 외래키 제약을 제거하는 선택을 했고 그 판단의 근거도 적어 두었습니다. 동의하든 않든 판단 과정이 드러나 있어 논쟁의 재료로 쓸 수 있습니다.

HikariCP Connection 고갈 문제

  • 블로그 · 작성자: zini9188.log (velog)
  • 한 줄 요약: 알림 기능을 붙인 뒤 서버가 죽은 원인이 커넥션 풀 고갈이었고, 그 뒤에 어떤 설정이 있었는지를 추적합니다.
  • 이런 사람에게: 커넥션 풀 크기를 늘리는 것으로 문제를 덮은 적이 있는 사람.

풀이 마르는 문제는 대개 풀 크기가 아니라 반납되지 않는 경로에 있습니다. 이 글은 오래 열려 있는 연결이 트랜잭션 범위와 겹치면서 커넥션이 붙잡히는 구조를 짚고, 프레임워크 설정 하나가 그 구조를 만들고 있었다는 것을 보여 줍니다. 코드를 나누어 연결 유지와 데이터 접근을 분리하는 쪽으로 해결하는데, 설정만 끄는 것보다 근본에 가깝습니다. 실시간 기능을 붙일 계획이 있다면 미리 읽어 둘 값어치가 있습니다.

MySQL 트러블슈팅 회고 (feat. 대소문자 구분)

  • 블로그 · 작성자: 숑숑의 삽질로그 · 숑숑 (velog)
  • 한 줄 요약: 애플리케이션은 다르다고 보고 데이터베이스는 같다고 본 두 문자열 때문에 같은 레코드가 갱신되고 삭제된 사건입니다.
  • 이런 사람에게: 문자열 비교를 언어와 데이터베이스 양쪽에서 하고 있는 코드를 가진 사람.

비교 기준이 계층마다 다를 수 있다는 것은 알고 있어도, 그것이 실제 사고로 이어지는 모습은 보기 어렵습니다. 이 글은 그 장면을 그대로 보여 줍니다. 원인은 콜레이션 설정 하나이고, 증상은 동기화 로직 전체가 이상해지는 것입니다. 해결책으로 비교 방식을 명시하는 방법과 스키마를 바꾸는 방법을 모두 제시해서 상황에 맞게 고를 수 있습니다. 다국어나 이메일 주소를 다루는 서비스라면 남 일이 아닙니다.

JPA가 Fetch Join에 대한 On절을 지원하지 않는 이유

  • 블로그 · 작성자: Tecoble (우아한테크코스) · 5기_베베
  • 한 줄 요약: 페치 조인에 조건을 붙이려고 우회한 결과 데이터베이스의 행이 실제로 삭제될 수 있다는 것을 보여 줍니다.
  • 이런 사람에게: ORM이 왜 어떤 문법을 막아 두는지 궁금했던 사람.

금지된 문법에는 대개 이유가 있고, 그 이유가 이렇게 무서운 경우는 드뭅니다. 조건을 걸어 일부만 가져오면 영속성 컨텍스트는 나머지가 삭제된 것으로 해석하고, 그 해석이 실제 삭제 쿼리로 나갈 수 있습니다. 예제가 붙어 있어 왜 그렇게 되는지 따라갈 수 있고, 결과적으로 ORM의 동작 모델을 한 단계 더 이해하게 됩니다. 기업 소속 블로그이지만 이 주제에 대해 한국어로 정리된 글 중 인과가 가장 선명해서 넣었습니다.

사람이 만든 사고, 그리고 그것을 쓴 용기

기술적 원인만큼이나 자주 일어나는 것이 절차의 사고입니다. 그리고 그것을 공개하는 데는 다른 종류의 용기가 필요합니다.

주니어 개발자의 첫 장애 회고 (새벽에 문자발송이라니..)

  • 블로그 · 작성자: 작심삼일을 꾸준히 · Jeonghwa (velog)
  • 한 줄 요약: 성능 테스트가 실제 문자 발송으로 이어져 새벽에 수십만 건이 나간 사고와, 그것을 되돌리려던 배포가 상황을 되살린 과정입니다.
  • 이런 사람에게: 스테이징 환경이 운영과 얼마나 분리되어 있는지 정확히 말할 수 없는 사람.

사고의 구조가 교과서적입니다. 테스트 환경이 실제 발송 경로와 연결되어 있었고, 문제를 인지하고 되돌리려던 배포가 헬스 체크 시간 초과로 자동 롤백되면서 문제의 코드가 살아 있는 버전으로 되돌아갔습니다. 하나의 실수가 아니라 두 개의 안전장치가 각각 어긋나면서 커진 사고이고, 그 연쇄가 시간 순으로 적혀 있습니다. 주니어 시절의 사고를 이 정도로 자세히 공개한 글은 흔하지 않고, 읽는 쪽에는 자기 팀의 어느 이음매를 봐야 하는지가 남습니다. 이 목록에 넣은 이유는 반면교사가 아니라 회고의 형식 자체가 좋기 때문입니다.

이 블로그의 관련 글과 도구

시리즈의 다른 글

Korean Dev Blog Curation 2 — Incident Retrospectives and Troubleshooting, 12 Posts I Opened and Checked

The Genre Korean Developers Write Best

Read Korean developer writing for long enough and one impression settles in. Conceptual explainers overlap heavily with what exists in English; incident retrospectives do not. They are about a specific thing that happened in the author's own service, which makes them irreplaceable, and the ones that do not hide the failure carry the highest information density.

The selection method is the same across the series. I found candidates by searching and then opened each post directly to check it, keeping the ones whose explanations were concrete and reproducible. For this post I added one more criterion: I preferred posts where the symptom and the cause turned out to be in different places — where the record of the wrong first suspicion survives. Those are closest to what real investigation feels like.

This list is an editorial pick, not a ranking. I did not measure views or popularity and have no way to. If someone else had chosen, the list would look different.

I gave priority to personal blogs. velog is heavily represented here, which reflects where this genre concentrates rather than any judgment that velog is better.

One note for readers outside Korea: every linked post is written in Korean. You will need Korean to read them, though the stack traces, packet captures, and configuration snippets that carry the argument read the same in any language.

All links were opened and checked directly on 2026-08-12. Personal blog posts can disappear or change addresses.

When the Cause Was Somewhere Else Entirely

There is one mark of a good troubleshooting post: the distance between the first suspicion and the actual cause is still visible.

사내 서비스 장애 해결 일대기 - p6spy 사용시 DB Routing 안됨, HikariCP 커넥션 누수

  • Blog · Author: Just Do It · uHan2 (velog)
  • One-line summary: Tracing a silent failure of read/write datasource routing back to a SQL logging library.
  • Read this if: You have attached a read replica and felt traffic was not splitting the way you expected.

This sits first because the cause was in a place nobody would think to suspect. A library added purely to print SQL nicely wrapped the connection in a way that bypassed the lazy-acquisition proxy, and routing quietly stopped working. It is the classic case of a developer-convenience tool changing runtime behavior, and that class of bug does not get caught in code review. The author also reported the finding upstream, which shows what the end of an investigation ought to look like.

Timeout이 작동하지 않은 이유

  • Blog · Author: sejinkim.log · sejin (velog)
  • One-line summary: Why a call stalled for fifteen seconds despite a three-second timeout — the answer is DNS resolution.
  • Read this if: You believe you have blocked the path by which an external API outage propagates into your service.

Assuming a configured timeout means safety is a common misconception, and this post pinpoints exactly where it breaks. Name resolution typically happens synchronously and outside the HTTP client's timeout, leaving a window your setting never covers. The post compares how different clients behave, so you finish knowing what to check in your own stack. It is short, but it changes how you read a timeout configuration afterward.

문제 현상 이면에 숨겨진 원인 찾기: TCP 통신 트러블슈팅

  • Blog · Author: JooSing (velog)
  • One-line summary: A symptom that looked like an application-level exception turns out, through packet analysis, to be a TCP half-close.
  • Read this if: You want to keep open the possibility that an apparent state-management bug lives in the network layer.

The title carries the thesis exactly. The exception message pointed at duplicate handling, but in reality a connection was left half-closed and blocking the next attempt. Looking only at application logs, you would never find this; capturing packets is the turning point. The cross-layer investigation is preserved as written, which is where you pick up the instinct for when to widen the scope of a search.

쿠버네티스 네트워크 문제 트러블슈팅

  • Blog · Author: hyeongjun-hub.log · hyeongjun Jo (velog)
  • One-line summary: A two-week hunt that traced recurring latency spikes in a production cluster to a single port blocked by a Helm chart.
  • Read this if: Unexplained intermittent latency has you staring at dashboards.

The scale of this one is striking. Three engineers worked it for two weeks, and the hypotheses they discarded along the way are still in the text. The final cause was that the port load balancer components use to recognize each other was blocked, so several nodes advertised the same virtual IP at once and the ARP tables destabilized. Since a chart default created the problem, this applies to any team running managed components. The investigation tools and their order are written down concretely, which makes it reproducible.

Getting Hands Dirty with Memory and CPU

Slow and dying are different problems, and the investigative tools differ too. The posts in this section actually use those tools.

Java의 heap dump를 이용한 OOM 원인 분석 with Eclipse MAT

  • Blog · Author: kyle's devlog (velog)
  • One-line summary: Tracing a dead grading server through a heap dump, then automating dump collection for the next incident.
  • Read this if: When an OOM hits, you do not know what to look at besides logs.

The cause itself is interesting: a user submitted an infinite loop, which produced an enormous output file, and code that read the whole thing into memory filled the heap. But the bigger value is the preparation for next time. The author built a mechanism that ships any dump straight to durable storage, so the next investigation does not have to wait for a reproduction. The order in which to read a histogram and a dominator tree is spelled out too, so it doubles as tool documentation.

3. [400분이 걸리는 10만 개의 알림 요청 시간을 줄여보자] 왜 CPU 스파이크가 발생할까? JVM WarmUP으로 해결해보자

  • Blog · Author: rawfish의 개발공부.log · 이건회 (velog)
  • One-line summary: CPU hitting 99 percent only right after deploy, explained down to the JIT compilation stage and solved with warm-up.
  • Read this if: You have accepted a few minutes of slow responses after every deploy as unavoidable.

Post-deploy slowness usually gets waved away as cold caches, but this post goes down to what the JIT compiler does when it has no cached profile. Thread dumps, monitoring screenshots, and load test results are all attached, so the claims are backed by observation. It is the third entry in a series, so it has context on both sides, and a concrete target of a hundred thousand notifications means the improvement comes out as a number. It also helps you tell apart the cases where warm-up is warranted from the ones where it is not.

[Java] 에러 스택 트레이스 최적화를 도와주는 -XX:+OmitStackTraceInFastThrow(사라진 예외 스택 트레이스)

  • Blog · Author: MangKyu's Diary (망나니개발자)
  • One-line summary: Why the stack trace of a repeatedly thrown exception disappears at some point, explained via a JVM optimization flag.
  • Read this if: You have suspected a logging misconfiguration when only exception names showed up with no stack trace.

This behavior blocks investigators precisely where it hurts: the more frequently an exception fires, the more likely its stack trace vanishes. Without knowing the cause is JVM optimization rather than logging, you can spend hours in the wrong place. A reproduction example is included, showing traces disappearing after repeated calls, so you can confirm it in your own environment. It is common knowledge to those who know and undiscoverable to those who do not, and having it written up in Korean is valuable.

Incidents Made by Databases and Connections

Plenty of outages leave the application code blameless and collapse at the data layer. This is especially true when the cause is a framework default.

[트러블슈팅 - DB] 외래키(Foreign Key)와 데드락(DeadLock) 그리고 쿼리 지연 실행

  • Blog · Author: hocaron.log · Hocaron (velog)
  • One-line summary: A deadlock with no explicit locking anywhere, traced to foreign key constraints combined with write-behind ordering.
  • Read this if: You have seen a deadlock log and found no locking code anywhere to explain it.

The post connects two pieces of knowledge: that changing a child row propagates a lock to the parent row, and that the ORM defers writes and then flushes them in a fixed order. Each is known on its own; the contribution here is showing that when they overlap the deadlock becomes deterministically reproducible. The chosen fix was removing the foreign key constraint, and the reasoning behind that choice is written down. Whether or not you agree, the reasoning is exposed, which makes it usable as material for an argument.

HikariCP Connection 고갈 문제

  • Blog · Author: zini9188.log (velog)
  • One-line summary: A server death after adding notifications, traced to pool exhaustion, and the setting sitting behind it.
  • Read this if: You have ever papered over a problem by raising the pool size.

A pool that runs dry is usually about paths where connections are not returned, not about pool size. This post identifies the structure where a long-lived connection overlaps the transaction scope and pins a database connection, and shows that one framework setting was creating that structure. The fix splits the code so connection-holding and data access are separated, which gets closer to the root than simply disabling the setting. Worth reading before you add any real-time feature.

MySQL 트러블슈팅 회고 (feat. 대소문자 구분)

  • Blog · Author: 숑숑의 삽질로그 · 숑숑 (velog)
  • One-line summary: Two strings the application considered different and the database considered identical, resulting in the same record being both updated and deleted.
  • Read this if: Your code compares strings in both the language and the database.

Everyone knows comparison semantics can differ per layer, but seeing that turn into an actual incident is rare. This post shows the scene directly. The cause is a single collation setting; the symptom is the entire sync logic going wrong. Both fixes — making the comparison explicit and changing the schema — are offered, so you can pick to fit your situation. If your service handles multiple languages or email addresses, this is not somebody else's problem.

JPA가 Fetch Join에 대한 On절을 지원하지 않는 이유

  • Blog · Author: Tecoble (Woowacourse) · 5기_베베
  • One-line summary: Working around a restriction on filtering a fetch join can cause rows to be genuinely deleted from the database.
  • Read this if: You have wondered why an ORM forbids certain syntax.

Forbidden syntax usually has a reason behind it, and rarely one this alarming. Filter to fetch only part of a collection and the persistence context interprets the remainder as deleted, and that interpretation can go out as an actual delete statement. Examples let you follow why, and you come away understanding the ORM's behavioral model one level deeper. It is a company-affiliated blog, but I included it because among Korean writeups on this topic the causal chain is the clearest here.

An Incident Made by People, and the Nerve to Publish It

Procedural accidents happen about as often as technical ones. Publishing them takes a different kind of nerve.

주니어 개발자의 첫 장애 회고 (새벽에 문자발송이라니..)

  • Blog · Author: 작심삼일을 꾸준히 · Jeonghwa (velog)
  • One-line summary: A load test that reached the real SMS path and sent hundreds of thousands of messages overnight — and the deploy meant to stop it that brought the problem back.
  • Read this if: You cannot state precisely how separated your staging environment is from production.

The structure of this incident is textbook. The test environment was wired to the real sending path, and the deploy intended to roll it back auto-reverted on a health check timeout, restoring a version where the offending code was still live. It grew not from one mistake but from two safeguards each failing in their own way, and that chain is laid out in chronological order. Publishing a junior-era incident in this much detail is uncommon, and what the reader takes away is which seam in their own team to go look at. I included it not as a cautionary tale but because the form of the retrospective itself is good.