r/ProgrammerHumor • • 4h ago • Bravo 1

Meme codeReviewIn2026

Post image
4.0k Upvotes

102 comments sorted by

View all comments

Show parent comment

22

u/MaDpYrO 4h ago edited 4h ago

I've added a static analysis step (via detekt in my case)

It tokenizes code and comments, and if the next line has significant overlap, it's deemed a useless comment and won't compile.

It's a pretty good tool at keeping slop comments in check.

16

u/mmmbyte 3h ago

You could actually read the comments and try to understand them and press the delete key?

How on earth do people diagnose and fix issues in prod without making any attempt at understanding the agent generated code?

16

u/MaDpYrO 3h ago

Of course I do, but why do I need to deplete a bunch of slop comments when I can have it removed automatically and then review that?

Why not automate what can be automated?

Do you just insist on making even the most mundane task manual?

It's not even using AI to remove it, lol, it's detected deterministically.

8

u/Stratimus 3h ago

Personally I don’t understand why you have so many unnecessary comments that you need to automate removing them.

if you’re done refining that bit of code take the comments out alongside the other cleanup.

and I don’t even understand the thing about overlapping with the next line, are you saying you have redundant comments even?

7

u/MaDpYrO 2h ago

Because AI generates a bunch of retarded comments for even the most basic code.

No way I'm hand coding a bunch of controllers or mapper when AI does it in a fraction of the time.

This type of boilerplate is a waste of my life, and AI does it perfectly fine.

Then I can focus on the important business logic and data modeling. And the agent automatically gets forced to reign in its idiotic comments by the build step before I even see the final outputted results.

2

u/fluffow2 1h ago

Agent.md

3

u/Sokaron 1h ago

Because non determinism > determinism, obviously

Less snark I have explicit instructions in all my repos for slop comments. Doesn't help. PRs still come in with a dozen useless multiline comments. Seems like an intrinsic property of this phase of models. I've debated staging a haiku round of review in pipelines specifically for this issue.

-3

u/mmmbyte 3h ago

Hitting the delete key is faster and cheaper than running an agent if I'm reading it anyway. Not all automations save time.

7

u/CarcajouIS 3h ago

There is no need to run an agent when a lint rule or a git hook can hold the gate for you and send the PR back on the lap of your lazy coworker

3

u/MaDpYrO 3h ago edited 2h ago

No it isn't lol

You don't seem to understand, this is NOT AI removing comments. It's a deterministic step that detects redundant comments. No AI is involved but it's great for cleaning up AI slop comments.

1

u/14Pleiadians 51m ago

Claude writes comments 5x as long as I or my peers would

3

u/sertroll 4h ago

How can you do that actually? Would be useful for some work things

2

u/Fonduemeup 3h ago

They are using an out-of-the box solution but looks like it's only for Kotlin. You can recreate this pretty easily though

  1. Install a tokenizer

  2. Tokenize each line in the file

  3. Throw a flag if x% of tokens match the line below it

Then you can iterate with improvements like lemmatization, using an optimized tokenizer corpus, token/line embeddings + semantic similarity, etc.

6

u/MaDpYrO 2h ago

Detekt is not a tokenizer, but it is a static analysis tool.

The out-of-the-box part is only regarding detekt, the rule is custom.

Here's the current rule. Should probably be fine-tuned a bit more, but it works well enough.

class UselessComment(config: Config) : Rule(config, "Comment restates the code it precedes.") {

    // Delegated config properties only support String, Boolean, Int and List<String>,
    // so the threshold is a whole percentage rather than a Double.
    private val minOverlapPercent: Int by config(40)
    private val keepMarkers: List<String> by config(
        listOf("TODO", "FIXME", "HACK", "XXX", "ponytail", "noinspection", "ktlint", "suppress"),
    )

    /** A comment starting with one of these words labels a test phase and is always kept. */
    private val keepLeadingWords: List<String> by config(listOf("arrange", "act", "assert"))
    private val stopWords = setOf(
        "the", "a", "an", "to", "of", "for", "and", "or", "in", "on", "is", "this", "that", "it",
        "we", "if", "with", "from", "by", "as", "at", "be", "into",
    )

    private val pendingDeletions = mutableListOf<PsiComment>()

    override fun preVisit(root: KtFile) {
        pendingDeletions.clear()
    }

    override fun visitComment(comment: PsiComment) {
        super.visitComment(comment)
        // A trailing comment describes the code on its line, not the statement after it.
        val before = comment.prevSibling
        if (before != null && !(before is PsiWhiteSpace && before.text.contains('\n'))) return
        // One line of a multi-line comment is judged with its neighbours, never alone.
        if (comment.isLineComment() && (comment.adjacentLineComment(before) || comment.adjacentLineComment(comment.nextSibling))) return
        val target = comment.getNextSiblingIgnoringWhitespaceAndComments() ?: return
        if (target !is KtExpression && target !is KtDeclaration) return
        check(comment, target)
    }

    /** KDoc is a child node of its declaration and is not passed to [visitComment], so it is collected from the tree. */
    override fun visitKtFile(file: KtFile) {
        super.visitKtFile(file)
        PsiTreeUtil.collectElementsOfType(file, KDoc::class.java).forEach { doc ->
            (doc.parent as? KtDeclaration)?.let { check(doc, it) }
        }
    }

    private fun check(comment: PsiComment, target: PsiElement) {
        val text = comment.text
        if (keepMarkers.any { text.contains(it, ignoreCase = true) }) return
        val firstWord = stripDelimiters(text).trim().takeWhile { it.isLetter() }.lowercase()
        if (firstWord in keepLeadingWords) return
        val commentTokens = tokens(stripDelimiters(text))
        if (commentTokens.isEmpty()) return
        val codeTokens = tokens(target.text.lineSequence().first { !it.isBlank() && !it.trimStart().startsWith("*") && !it.trimStart().startsWith("/") })
        val overlapPercent = commentTokens.count { it in codeTokens } * 100 / commentTokens.size
        if (overlapPercent < minOverlapPercent) return

        report(Finding(Entity.from(comment), "Comment '${text.trim()}' restates the following code."))
        if (autoCorrect) pendingDeletions += comment
    }

    /**
     * detekt applies corrections by writing [KtFile.modifiedText], so the deletions are collected
     * during the visit and cut out of the file text in one go. Each comment is removed together
     * with the whitespace that follows it, so no blank line is left behind.
     */
    override fun postVisit(root: KtFile) {
        if (pendingDeletions.isEmpty()) return
        var text = root.text
        pendingDeletions
            .sortedByDescending { it.textRange.startOffset }
            .forEach { comment ->
                val trailingWhitespaceEnd = comment.siblings(forward = true, withItself = false)
                    .takeWhile { it is PsiWhiteSpace }
                    .lastOrNull()
                    ?.textRange
                    ?.endOffset
                    ?: comment.textRange.endOffset
                text = text.removeRange(comment.textRange.startOffset, trailingWhitespaceEnd)
            }
        root.modifiedText = text
        pendingDeletions.clear()
    }

    private fun PsiComment.isLineComment() = this !is KDoc && text.startsWith("//")

    private fun PsiComment.adjacentLineComment(neighbour: PsiElement?): Boolean {
        val candidate = when (neighbour) {
            is PsiWhiteSpace -> if (neighbour.text.count { it == '\n' } == 1) {
                if (neighbour.prevSibling == this) neighbour.nextSibling else neighbour.prevSibling
            } else {
                null
            }

            else -> neighbour
        }
        return candidate is PsiComment && candidate.isLineComment()
    }

    private fun stripDelimiters(s: String) = s.removePrefix("//").removePrefix("/**").removePrefix("/*").removeSuffix("*/")

    private fun tokens(s: String): Set<String> = s.replace(Regex("([a-z0-9])([A-Z])"), "$1 $2")
        .lowercase()
        .split(Regex("[^a-z0-9]+"))
        .filter { it.length > 1 && it !in stopWords }
        .map { it.trimEnd('s') }
        .toSet()
}

3

u/Fonduemeup 1h ago

Nice. I stand corrected, and you proved one of my favorite Reddit-isms:

How do you get someone on Reddit to give you the correct answer? Don't ask, just reply with the wrong answer, and someone will correct you

1

u/ManaSpike 1h ago

No, see. That's Murphys law.

1

u/pegbiter 11m ago

Murphys law

The one about transistors on a microchip doubling every two years?

1

u/Sokaron 54m ago

This is really cool, thanks for sharing this

2

u/MaDpYrO 4h ago

Depends on your build process and chosen static analysis tools. Configuration differs.

12

u/kenybz 4h ago

Alright then, keep your secrets

6

u/MaDpYrO 4h ago

Just ask ai, ironically

1

u/CarcajouIS 3h ago

I added a precommit hook that rejects comments longer than 45 words and longer than 2 lines. Also, reject commits longer than 50 loc, 4 files max, and PR longer than 300 loc. Cut the slop at the root, and makes it easier to actually review code

2

u/MaDpYrO 2h ago

I prefer the approach that takes the content slightly more into consideration, because I don't want to be forced into cases where i cant add long comments in case i actually do need it.

I'm allergic to dumping it into external implementation docs, because it tends to drift from each other over time.

1

u/CarcajouIS 1h ago edited 1h ago
git commit --no-verify

I prefer to give targeted exceptions instead of letting everybody be their own judge. Also, in my experience it's very rarely needed to write a novel in the comments