UseJunior Book a Demo

Engineering Post

Round-trip fidelity and tracked changes: where safe-docx and python-docx differ

python-docx is excellent at generating and modifying Word documents, but tracked changes and redline output are not its job. This is why brownfield editing of existing .docx files — with revisions preserved — needs a different tool.

EngineeringDOCXOOXMLpython-docxTracked Changessafe-docx

If you maintain a document pipeline, the failure you remember is never the one that crashed. It is the one that "mostly worked": numbering that drifted, comments that vanished, a redline that opened strangely in Word. So when people ask how safe-docx compares to python-docx, the honest answer starts by saying what python-docx is genuinely good at — and then drawing the line where its job ends and brownfield editing begins.

python-docx is a generation and modification library

python-docx is a mature, well-loved library for creating Word documents and making structural edits — adding paragraphs, styling runs, building tables, setting section properties. If your task is "produce a new .docx from data," it is a natural fit, and nothing here is a criticism of it at that job.

What it is not built for is tracked changes. Word records edits as OOXML revision markers — <w:ins> for insertions, <w:del> for deletions, move wrappers, and property-change records — and python-docx does not expose those as first-class objects. Producing review-ready redlines, or reading the revisions already in a document, sits outside its model. That gap is long-standing and well known in the community, which is why people reach for XML-level workarounds when they need it.

Brownfield editing has a different contract

safe-docx starts from the opposite end of the stack. The job is to take an existing .docx — one a lawyer or counterparty authored, with its own styles, numbering, comments, and possibly existing tracked changes — and edit it surgically while preserving everything the edit did not touch. Two requirements fall out of that:

  1. Round-trip fidelity. Parsing and re-serializing the document must not perturb the parts you did not edit. Element structure, attributes, and text have to survive the trip byte-for-byte where nothing changed. The XML round-trip scenarios exercise exactly this — parse, serialize, and confirm the structure is preserved.

  2. Revision semantics as a first-class output. Edits can be wrapped as tracked changes so they show up in Word's review pane next to any human revisions, and two versions can be compared into a tracked-changes document via compare_documents. Existing revisions can be read back out as structured JSON with extract_revisions, or resolved with the accept-changes primitives — see the accept-tracked-changes scenario for how a move is resolved into destination-only text.

Where each one belongs

This is not "better library, worse library." It is a stack question:

The feature-by-feature breakdown — tracked-change handling, OOXML coverage, and round-trip behavior — is on the safe-docx vs python-docx comparison. And because the claims above should be inspectable rather than taken on faith, the underlying behaviors are written up as evaluation scenarios with real fixtures and expected output, with the full test run in the public Allure report. Where the edges still are is stated plainly in known limitations.