Skip to main content
Login

The GDScript Code Review Checklist

A six-category checklist grounded in the Godot 4.6 docs: version, lifecycle, signals, scene tree, types, resources. Use it before you merge.

Godot doesn’t have a standard code review checklist. The engine has a style guide, a warning system, and PR review guidelines for engine contributors. But if you’re building a game with a small team or solo and pushing code to GitHub, there’s no canonical “check these things before you merge” resource.

This is that resource.

Every check below is grounded in the official Godot 4.6 documentation. Where a check references a specific docs page, it’s linked. The checklist is organized into six categories that cover the most common issues we see in Godot pull requests, especially in projects using AI-assisted development where wrong-version patterns and plausible-but-broken code show up regularly.

Use it before you merge. Print it. Put it in your repo’s CONTRIBUTING.md. Or put Surmado Code Review on the repo and have Scout check the diff automatically.


1. Version correctness

Is this code actually written for Godot 4?

This is the highest-priority category for any project that uses AI assistance. LLMs are trained on years of Godot 3 tutorials, forum answers, and documentation. They will confidently produce Godot 3 code in a Godot 4 project. The official migration guide lists hundreds of renames and breaking changes. The built-in project converter catches many of them, but Godot’s own docs warn that upgraded scripts will likely still contain errors and much of the upgrade remains manual.

Even after migration, AI can reintroduce old patterns in new files. This category catches both legacy code and fresh AI-generated code that learned from the wrong version.

Check: Are all node class names current? Scan for renamed classes. The most common:

  • KinematicBody2DCharacterBody2D
  • KinematicBodyCharacterBody3D
  • Position2DMarker2D, Position3DMarker3D
  • SpatialNode3D
  • SpriteSprite2D

The full rename list is in the migration docs.

Check: Are method signatures current? Several commonly used methods changed signatures or were renamed:

  • move_and_slide() no longer takes arguments. It reads from the node’s built-in velocity property. (CharacterBody2D docs)
  • instance()instantiate()
  • set_shader_param()set_shader_parameter()
  • map_to_world()map_to_local() (GridMap and TileMap)
  • change_scene()change_scene_to_file()
  • Thread.start(self, "__method", args)Thread.start(__method.bind(args))
  • Thread.is_active()Thread.is_alive()

Check: Are signal connections using the callable syntax? The old connect("signal_name", target, "method_name") pattern is deprecated. Godot 4 uses callable-based connections:

# Old (Godot 3)
button.connect("pressed", self, "_on_button_pressed")

# Current (Godot 4)
button.pressed.connect(_on_button_pressed)

The callable form gives compile-time validation. String-based connections silently break at runtime if you rename the method. (Signal docs)

Check: Are constants and signal names current? Color constants changed format (e.g., Color.palegreenColor.PALE_GREEN). Some signal names were renamed (e.g., CanvasItem.hidehidden). Check the migration docs rename tables.


2. Lifecycle callbacks

Is code running in the right callback?

This is the most dangerous category because wrong-callback code compiles, runs, and appears to work. It fails under real conditions: different frame rates, physics edge cases, or hardware variation. Godot’s notification docs are explicit about which callback serves which purpose.

Check: Is physics movement in _physics_process()? _process() runs every rendered frame and is framerate-dependent. _physics_process() runs at a fixed rate (60 times per second by default). Any code that calls move_and_slide(), move_and_collide(), or directly manipulates a physics body’s velocity belongs in _physics_process(). (Physics introduction)

Check: Is input handled in the right place? For discrete events (key press, button click), prefer _input() or _unhandled_input(). For continuous state (is the key held down), _process() or _physics_process() with Input.is_action_pressed() is appropriate. Polling input every frame in _process() when you only need it for physics movement is a common AI-generated pattern.

Check: Is _ready() doing setup, not ongoing work? _ready() runs once when the node enters the tree. Children run _ready() before parents. If initialization depends on a sibling or parent node being ready, consider using await owner.ready or deferring the setup call.


3. Signal hygiene

Are signals keeping code decoupled?

Godot’s signal system lets objects react to events without directly referencing each other. The practical rule is “call down, signal up”: parent nodes can call methods on children directly, but children should communicate upward through signals. AI-generated code frequently violates this by having children reach into parents or siblings.

Check: Are signals used instead of direct parent references? If a child node needs to notify its parent that something happened, it should emit a signal. The parent connects to that signal. The child should not call get_parent().some_method().

Check: Are signal connections in the right place? The node that cares about the event should own the connection. Typically, parent nodes connect to their children’s signals in _ready(). Avoid connecting signals in deeply nested or distant parts of the tree.

Check: Are signals disconnected when needed? If nodes are dynamically added and removed, lingering signal connections can cause errors or unexpected behavior. Check whether disconnect() or queue_free() cleanup is needed.


4. Scene tree architecture

Does the code assume a fixed tree structure?

Godot is built around composable scenes that can be instanced anywhere. Hardcoded paths and parent references break this composability. The scene organization docs say reusable sub-scenes should work without requiring details about their environment.

Check: Does the script navigate upward with ..? get_node(".."), get_parent(), and paths like ../../Player are brittle. They break if the scene is reparented, reused in a different context, or restructured. The nodes and scene instances docs say avoiding .. is best practice.

Check: Should this be an @export reference instead of a hardcoded path? @export var target: Node lets you wire references in the editor. It’s more robust than get_node("../Player") because it survives tree restructuring and makes dependencies explicit.

Check: Should this use groups instead of direct references? Groups let you tag nodes and access them without knowing their path. get_tree().get_nodes_in_group("enemies") is more resilient than iterating over children by name.

Check: Are parent-child relationships meaningful? The docs say parent-child relationships should represent real containment: if removing the parent shouldn’t remove the child, the child may belong elsewhere in the tree. AI often nests nodes for convenience rather than semantic correctness.


5. Type safety and warnings

Is the project using Godot’s built-in safety tools?

GDScript supports static typing and has a built-in warning system. Both are optional but significantly reduce runtime surprises. The docs say static types help detect errors without running the code and improve editor autocompletion.

Check: Are function parameters and return types annotated?

# Untyped (works but risky)
func take_damage(amount):
    health -= amount

# Typed (catches errors at parse time)
func take_damage(amount: int) -> void:
    health -= amount

Best practice is all or nothing. The docs warn that mixing typed and untyped code in the same project creates confusion.

Check: Are warnings taken seriously? Godot’s warning system catches unused variables, unreachable code, and patterns that may lead to runtime errors. Warnings can be escalated to errors in project settings. If your project ignores all warnings, bugs hide.

Check: Are @warning_ignore annotations justified? Godot supports @warning_ignore("unused_variable") and similar annotations. Each one should be intentional, not a way to silence noise. During review, ask: is this ignore hiding a real issue, or is it a deliberate exception?


6. Resource management

Is the code loading, creating, and freeing things safely?

Godot’s resource system is powerful but has patterns AI frequently gets wrong, especially around loading, instantiation, and cleanup.

Check: Is load() vs preload() intentional? preload() loads at compile time and requires a constant string path. load() runs at the line it’s called. Use preload() for resources you always need. Use load() for resources loaded conditionally or dynamically. AI tends to use load() everywhere because it’s more flexible, but this creates unnecessary runtime loading.

Check: Are packed scenes instantiated correctly? In Godot 4, saved scenes are PackedScene resources. You call instantiate() (not instance(), which is the Godot 3 name) to create the node tree. Check that instantiated scenes are added to the tree with add_child().

Check: Are nodes freed safely? Use queue_free() unless you have a specific reason not to. free() immediately destroys the node, and any references to it become null instantly. queue_free() waits until the end of the current frame, which is safer. The docs recommend queue_free() as the default.

Check: Are autoloads used sparingly? Autoloads are globally accessible, but they’re still nodes in the scene tree. They must never be removed with free() or queue_free() at runtime or the engine will crash. AI-generated code sometimes treats autoloads as disposable managers. Review whether global state genuinely needs to be global or could be a resource or a group instead.


What this checklist doesn’t cover

This checklist covers code review: correctness, architecture, and patterns. It does not replace:

  • Linters and formatters. Tools like gdscript-toolkit and Godot’s built-in warnings catch syntax, formatting, unused variables, and some type issues. Use them. They’re complementary.
  • Playtesting. Code review catches structural problems. It doesn’t catch “this doesn’t feel right” or “the jump is too floaty.” That’s testing.
  • Architecture design. This checklist asks “is the architecture sound?” It doesn’t tell you what architecture to use. For that, read Godot’s best practices section.

Automating the checklist

You can run through this list manually on every PR. Or you can automate it.

Surmado Code Review for Godot checks every push to a GitHub pull request against the official Godot 4.6 documentation, your repo context, and your STANDARDS.MD. It catches version mistakes, lifecycle misuse, deprecated patterns, brittle scene tree paths, and whatever custom rules you define.

Free for 10 PRs a month. $15/month for 100. No per-seat pricing.

The checklist is what to review. Surmado Code Review is how to review it on every PR without staring at diffs.


Install Surmado Code Review for Godot (Free) →

Not sure about a specific Godot API or pattern? Ask GDScout against the official 4.6 docs. Free, with citations.

Questions and Answers

Is there a GDScript code review checklist? Yes. The GDScript Code Review Checklist covers six categories: version correctness (Godot 3 vs 4 patterns), lifecycle callbacks (_process vs _physics_process), signal hygiene, scene tree architecture, type safety and warnings, and resource management. Each check is grounded in the official Godot 4.6 documentation.

What should I check in a Godot code review? A Godot code review should verify version correctness (no Godot 3 patterns in a Godot 4 project), correct lifecycle callback usage, callable-based signal connections, composable scene tree architecture without brittle parent paths, static typing and warning compliance, and safe resource loading and node cleanup. The GDScript Code Review Checklist provides specific checks for each category with links to official documentation.

Does Godot have a code review tool? Godot’s built-in tools include a GDScript warning system and static typing support, which catch syntax and type errors. For PR-level code review, Surmado Code Review for Godot checks GitHub pull requests against the official Godot 4.6 documentation and a team’s STANDARDS.MD. It catches version mistakes, lifecycle misuse, deprecated patterns, and architecture issues that linters do not cover. Free for 10 PRs per month.

What are the most common mistakes in AI-generated Godot code? The most common mistakes are using renamed Godot 3 node types and method signatures, placing physics movement in _process() instead of _physics_process(), using deprecated string-based signal connections, hardcoding brittle parent and sibling node paths, and using load() instead of preload() for compile-time resources. These patterns pass linting and compile without errors but cause runtime failures or inconsistent behavior.

How is a code review checklist different from a GDScript linter? A GDScript linter catches syntax errors, formatting issues, unused variables, and some type problems. A code review checklist evaluates higher-level concerns: is this the correct engine version API, is movement code in the correct callback, are signal connections modern, is the scene tree architecture composable, and does the code follow project-specific standards. Linters and code review are complementary.

What is STANDARDS.MD? STANDARDS.MD is a Markdown document that defines the coding standards a team enforces during code review. In Surmado Code Review, STANDARDS.MD is created through a conversation with Scout, Surmado’s AI agent, rather than by editing a config file. The document can include Godot-specific rules such as “always use _physics_process for CharacterBody movement” or “no parent path navigation with get_node(’..’).”

Ready to Take Action?

Scout researches your brand in ~15 minutes.