Writing Maintainable Code
Writing code that works is only half the battle. Writing code that's easy to read, understand and modify months later is what separates a good developer from a great one. As your project grows, you'll find yourself spending far more time reading and maintaining existing code than writing new code. These practices make that experience much more enjoyable, for both yourself and anybody else that works on your project.
Use Meaningful Names
Variable and function names should be easily descriptive for what purpose they serve, not saving a few keystrokes.
- Bad
local p = PlayerPedId()
local c = GetEntityCoords(p)- Good
local playerPed = PlayerPedId()
local playerPos = GetEntityCoords(playerPed)Someone reading your code shouldn't have to guess what p or c actually means.
Keep Functions Small
A function generally should do one thing. Instead of creating one large function that handles multiple responsibilities like SpawnVehicle(), break it up into smaller functions like LoadModel(), CreateVehicle(), SetupVehicle(), GetVehicleKeys(). Smaller functions are easier to read, test, debug and can be reused instead of rewriting the same code a dozen times.
Avoid Deep Nesting
Code that is nested several levels deep quickly becomes difficult to follow.
- Bad
local function doThing()
if player then
if player.job then
if player.job.name == "police" then
-- do thing
end
end
end
end- Good
local function doThing()
if not player then return end
if not player.job then return end
if player.job.name ~= "police" then return end
-- Do thing
endUsing early returns reduces indentation and makes your intent much clearer.
Don't Repeat Yourself (DRY)
If you find yourself copying and pasting code, ask yourself if it should be a function instead.
- Bad
-- In function one:
SetEntityHeading(vehicle, heading)
SetVehicleOnGroundProperly(vehicle)
-- In another function:
SetEntityHeading(vehicle, heading)
SetVehicleOnGroundProperly(vehicle)
-- In another function
SetEntityHeading(vehicle, heading)
SetVehicleOnGroundProperly(vehicle)- Good
-- Make a re-usable function that you can reference in other functions.
local function setupVehicle(vehicle, heading)
SetEntityHeading(vehicle, heading)
SetVehicleOnGroundProperly(vehicle)
endIf you need to change the logic later, you only need to change it in one place.
Keep Globals to a Minimum
Keep the global scope as clean as possible, when you can always localize functions and variables.
- Bad
MovingObject = false -- Variable only referenced in this one file- Good
local movingObject = false -- Variable only referenced in this one file, defined locally.Comment the "Why", not the "What"
Good comments will explain why something is happening, not what is happening. People reading your code should be able to understand what is happening just by reading it.
- Bad
-- Sets the player's health to 200
SetEntityHealth(playerPed, 200)- Good
-- Give the player temporary health while their character loads.
SetEntityHealth(playerPed, 200)If the code clearly explains what it's doing, there's no need to repeat it. Additionally, you shouldn't just spam comments throughout your entire codebase, if it isn't obvious why something is done in a way that's when we'd recommend adding a comment to explain why.
Be Consistent
Consistency is more important than personal preference. Choose a naming style and stick with it, for example:
playerPedplayerCoordsvehicleEntityspawnPosition
Don't mix multiple naming conventions throughout the same project. Consistency also applies to formatting. Whether you use tabs or spaces, snake_case or camelCase, choose one style and stick with it throughout your project. Consistent formatting makes code much easier to read.
Remove Dead Code
Commented-out code quickly becomes clutter. If you're no longer using it, delete it. That's one of the reasons why we utilize version control like Git, you can always recover the old code if you need it. If you aren't using the code anymore, delete it.
Don't Optimize Too Early
While this sounds counter productive, focus on writing readable and functioning code before micro optimizations. Write code that works, profile it, and then optimize the parts that actually need it.
Premature optimization often makes code more difficult to understand without providing any meaningful performance improvements.
Leave the Code Better Than You Found It
Whenever you're working in a file, look for small improvements you can make. Maybe that's renaming a confusing variable, removing unused code, improving a comment or something else. Small improvements add up over time and make your project much easier to maintain.
Final Thoughts
Maintainable code isn't about writing clever code, it's about writing code that's easy for someone else to understand. That "someone else" usually ends up being you, six months from now.
Future you will thank present you for writing clean, organized and readable code. Here's some more resources for you to reference:
Code is read far more often than it's written. Optimize for readability first.
