How Roblox Actually Works Under the Hood
Most people who start building in Roblox think the whole platform runs on Lua. That's only half true. The scripting layer is Lua-based, but the engine itself, the render pipeline, physics, networking, and all the system infrastructure are written in C++. That split between the game logic you write and the C++ core that actually executes it is where most confusion comes from. I spent years trying to trace why certain behaviors happened the way they did, and it always came back to that boundary between Lua and C++. The scripting language on top is called Luau now, not plain Lua. It's a dialect created by Roblox that started as a fork of Lua 5.3 and has been heavily customized. They added optional static typing, better error handling, and various performance optimizations. If you're writing scripts in Roblox Studio right now, you're writing Luau, even if the tooltips and documentation still casually call it Lua. The difference matters because some syntax that works in standard Lua will silently fail or behave differently in Luau, and the reverse is also true. The C++ side of things isn't something you'll touch directly unless you're doing engine-level work or writing external tools. The relevant parts for most developers are the services that bridge C++ into Luau, things like Workspace, Players, Lighting, and the whole instance hierarchy. Every BasePart, every Model, every Script object in your game is an instance backed by C++ code. When you call something like part.Position, that property access goes through a binding layer that translates Luau calls into C++ operations.
Roblox Studio itself is a Windows application built with C++ and Qt. It's got a full IDE with a output window, command bar, profiler, and debugging tools. The command bar alone is worth knowing inside out. I used it extensively for batch operations, like when I had a map with over 4,000 parts and needed to clean up orphaned instances after a merge went wrong. Running a script through the command bar processed everything in about 90 seconds. Doing it manually would have taken hours and probably still wouldn't have been thorough enough. One thing that catches people off guard is how Luau handles variable scoping differently from older Lua versions. In standard Lua 5.2 and below, local variables declared inside control flow blocks like if or while loops leak into the outer scope. Luau fixed that. The behavior matches what most developers expect now. But if you ever export code from an older Roblox project or import a library written for Lua 5.1, you might hit subtle bugs where a variable seems to carry a value across loop iterations when it shouldn't. That happened to me once with a data persistence system that randomly saved corrupted values. Took me three days to trace it back to a stale local from an outer scope that shouldn't have existed. The physics engine is another C++ component you interact with indirectly through properties and events. Roblox uses a custom physics implementation, not an off-the-shelf solution like PhysX or Bullet, though I'm not entirely sure which foundational libraries feed into it. What matters in practice is that BodyVelocity, AssemblyLinearVelocity, and the newer VectorForce system don't always behave the way you'd expect from a classical physics perspective. Forces accumulate differently depending on whether you apply them through LegacyBodyMovers or the new Constraint system. I had a vehicle controller that worked perfectly in isolation and then behaved completely differently once I added collision checks against terrain. Switching from BodyVelocity to AssemblyLinearVelocity with manual delta-time scaling fixed it, but it required rewriting about 200 lines of movement code. The old system still exists for backwards compatibility, but it's been deprecated for a long time now.
Networking is where the architecture gets interesting. Client-server replication is automatic for most instance properties, but the moment you need custom data flowing between client and server, you're working with RemoteEvents and RemoteFunctions, which are Luau wrappers around Roblox's replication layer. The network stack is not transparent. Latency, packet loss, and client-side simulation all affect how data arrives. I once built a shooting mechanic where the server validated shots based on client input. Under normal conditions it worked fine. When players hit certain areas with high ping, the hit detection became unreliable because the client position at fire time didn't match the server's understood position at validation time. The fix was implementing a lag compensation system using PredictedPosition and storing the player's last known valid positions per tick. It added maybe 40 lines of code but eliminated the entire class of desync bugs. The Luau type system is optional but worth using if you're building anything larger than a simple script. You annotate types with comments like @type String or @return Int, and the Luau language server will catch mismatches before you even run the game. This caught more bugs in my projects than any runtime check ever did. One common trap is assuming that Instance:GetDescendants() returns a stable list. It doesn't. If anything changes in the hierarchy during iteration, the iterator can skip elements or throw errors. I worked around this by copying the results into a temporary table first, then iterating that copy. It's a small cost for correctness. Rendering is handled by Roblox's own graphics layer, which supports Direct3D 11 and OpenGL depending on the client machine. Shaders are not exposed to Luau developers in any meaningful way. You can tweak Material properties, use SurfaceAppearance objects, and adjust lighting parameters, but you're working within constraints that Roblox defines. If you need custom post-processing or procedural geometry, you're generally out of luck inside the engine. Some developers get around this by using BillboardGuis or ScreenGuis layered on top of canvas drawing, but that's a workaround, not a real solution.
Get the Full Details

DataStore service is another area where the documentation oversells reliability. It's easy to assume that DataStore:SetAsync() either succeeds or fails cleanly. In practice, timeouts, throttling, and concurrent access can cause silent failures if you're not checking return values and error codes properly. I learned this the hard way when a game update wiped progress for roughly 12 percent of players because I wasn't handling DataStore errors with proper retry logic. Adding exponential backoff retries and separating write operations into individual player tasks reduced the failure rate to under 0.5 percent. That kind of detail doesn't come up in beginner tutorials. Profiler timing is built into Studio and useful for finding bottlenecks, but it measures total frame time across all systems. A script that appears to take 2ms in the profiler might actually be doing most of its work during garbage collection cycles that aren't attributed to the script itself. Luau's garbage collector runs concurrently but can still cause frame hitches in tight loops with lots of temporary object creation. I reduced hitches in a pathfinding-heavy system simply by pooling trajectory objects instead of creating new ones each frame. The change dropped peak memory allocation by about 70 percent and smoothed out frame times noticeably. If you want to extend Roblox beyond what the built-in tools provide, there are a few paths. External tools can connect through the Roblox API using HTTP requests, and some developers build separate management dashboards this way. There's also the Plugin API for Studio extensions, which lets you create custom editor tools using Luau. These are sandboxed inside Studio and can't affect live games, but they're useful for workflow automation. I wrote a plugin that auto-generated navigation mesh regions for large maps, cutting what used to be a half-hour manual process down to under two minutes. It wasn't perfect and required some manual tweaking afterward, but it was far better than doing it by hand every time.
The documentation at dev.roblox.com is decent but not comprehensive. Several edge cases, especially around replication timing and constraint behavior, aren't well covered there. The community forums and Discord servers fill in a lot of gaps, but the information is scattered and sometimes outdated. I keep a personal reference document with workarounds and patterns that actually work in production, because relying solely on official docs led to repeated issues in my projects. Luau compiles scripts to bytecode before execution. This bytecode is cached on the client, which speeds up loading but means that updates to scripts don't always propagate immediately during testing. I've wasted time debugging behavior that seemed impossible, only to realize the client was running stale bytecode from a previous test session. Clearing the cache or using the stop button and relaunching cleanly usually resolves it. This is more of an annoyance than a fundamental problem, but it trips up everyone at least once. For anyone doing serious development, learning the difference between what happens in Luau versus what happens in C++ is essential. A lot of seemingly arbitrary behavior comes from that boundary. When in doubt, check whether a property is replicated, whether an event fires on the client or server, and whether the operation goes through the Luau interpreter or directly into C++. Those three questions answer most of the confusing issues that come up during development.