As a WordPress developer diving into Moodle extension development for the first time, I discovered surprising approaches that challenged my development perspectives in unexpected ways.
Last year, a customer approached me about building a Moodle extension for them, having previously worked with them on a WordPress plugin for their SaaS. Their pitch was memorable: “Moodle has an outdated codebase, lots of technical debt, and poor documentation. As a WordPress dev, you should feel right at home, right?” They weren’t wrong.
My only previous experience with Moodle was as a student. Before diving into development, I wanted to thoroughly understand the platform’s principles, architecture, and ecosystem. I set up a demo site, experimented with its features, studied the documentation and coding standards, and completed several Moodle Academy courses until I felt confident enough to start development.
Working with Moodle turned out to be really interesting, so I wanted to share what I learned. This post presents my perspective as a first-time Moodle extension developer, focusing primarily on common CMS features rather than LMS-specific functionality.
The Good
Stricter Rules
Moodle enforces a structured approach to extensions through predefined types such as activities, registration providers, payment gateways, and security modules. Coming from WordPress, where plugins can freely implement multiple functionalities, this was an interesting change. Each extension type has its own specific rules and APIs, which initially feels restrictive but brings considerable benefits. The clear separation of concerns and focused functionality leads to better organized code. Standardized APIs for each extension type make maintenance and security oversight more manageable.
WordPress’s flexible plugin system, with fewer restrictions, is often credited for driving innovation in its ecosystem. But rules also promote better organization and maintainability. The extension types cover most aspects of a Moodle installation.
Code Style, Quality & Naming
The most significant aspect of Moodle development is its rigorous approach to code standards. To get your extension listed in the official repository, you must strictly adhere to Moodle’s coding standards and naming conventions. This is enforced through a mandatory “plugincheck“/”codecheck” tool, available as both an extension and CLI tool. You’re encouraged to run these checks in CI, maintain a public Git repository with issue tracking, and make CI results visible to reviewers. While I may have personal reservations about some aspects of the code style (more on that below), the consistency across all Moodle extensions and core code is invaluable, making the entire codebase more navigable and maintainable for developers.
These standards go beyond mere aesthetics – they enforce code quality practices that enhance security across all extensions. The strictly defined directory structure and namespace conventions enable features like automatic class loading, feature auto-discovery (e.g. tasks, checks, backup and privacy APIs).
As a newcomer to the ecosystem, these strict conventions proved surprisingly helpful rather than restrictive. While some older core code doesn’t fully adhere to these standards (discussed in the technical debt section), the experience has even influenced my perspective on WordPress coding standards.
Focus on A11y
Given Moodle’s widespread use in universities and public education, accessibility (a11y) is a core priority. While I can’t personally verify all accessibility features, the platform’s commitment to a11y is evident throughout the system. The documentation, academy materials, and examples consistently emphasize accessibility considerations. What’s particularly impressive is how the core APIs enforce accessibility requirements by design. Components, registration APIs, and other platform features have accessibility built into their foundation, making it natural for developers to create accessible extensions without requiring extensive additional effort.
Core APIs
Moodle provides a comprehensive set of core APIs organized into categories, with access restricted based on your extension type. While I found the API design and documentation okay-ish, the mere existence of these standardized APIs is impressive, especially coming from a WordPress background.
The Admin Settings API particularly stands out. It streamlines the process of registering settings, handling everything from basic configuration to validation and form rendering. The result is a consistent settings interface across all extensions, with built-in searchability, config.php support, and accessibility. In my project, I implemented a custom table component for managing multiple API credentials, and the API made it straightforward to maintain UI consistency and accessibility throughout.
For activity modules like ours, Moodle provides a structured approach through `mod_form.php`, utilizing the Forms API. While it may be built on the older QuickForm library, having a dedicated forms API at all is remarkable. The API supports conditional logic, validation rules, and ensures consistent layout across all extensions. As a WordPress developer, finding such a comprehensive forms system built into the core platform was a pleasant surprise.
A relatively recent addition is the Privacy API, which brings transparency to data collection practices. Extension developers are encouraged to declare what data they collect, making this information available system-wide. Users can request or delete their data through standardized interfaces. This kind of privacy-first approach through API design is something I believe every modern platform should implement.
The Backup API proved particularly valuable for our project, enabling teachers to export, duplicate, and reimport course activities. Given that Moodle extensions use dedicated database tables rather than WordPress’s post/postmeta approach, having a standardized backup and import system is crucial. This built-in functionality also helps prevent the kind of security vulnerabilities often seen in WordPress plugins during import/export operations.
Standardized UIs
The various APIs mentioned above all leverage the Output API, ensuring consistent component usage throughout the platform. This creates a unified user interface with accessibility built in from the ground up. As someone passionate about improving UIs through component-based design, this systematic approach to interface consistency really resonated with me.
Included Components / Core Plugins
Moodle’s architecture takes a notably modular approach, with much of its core functionality implemented as core plugins shipped with the platform. These extensions follow the same types and structure available to third-party developers, meaning they must use the same APIs as external extensions. This approach leads to more robust core APIs, as they’re truly designed for extension development rather than relying on special cases. This contrasts with WordPress, where core functionality often includes special handling for features like post tags and categories scattered throughout the codebase.
The breadth of functionality included out of the box surprised me. OAuth-based authentication and SSO, SMTP configuration, notifications, PayPal-based enrollment payments – it’s all there from the start.
While some Moodle administrators might find this initial setup overwhelming, I think it provides a solid foundation for most use cases. This comprehensive approach made me reconsider WordPress’s minimal core philosophy – perhaps more commonly-needed functionality should be included in core (or as “canonical plugins”) rather than requiring third-party solutions for basic features.1
Database Structure
Moodle takes a refreshingly straightforward approach to data storage: extensions must use their own database tables. Our activity module used dedicated tables for storing activities and results. The platform provides a robust migration system using XML files for initial installation and an `upgrade.php` for version updates. There’s even a visual database designer/editor in the admin interface. This abstraction layer enables Moodle to support multiple database types while encouraging developers to design proper data structures for their needs.
This approach stands in stark contrast to WordPress, where custom tables are often viewed with skepticism, and developers (myself included) frequently attempt to squeeze diverse data types into the posts/users/terms tables and their respective meta tables.
The Bad
Code Style
While I praised the enforcement of coding standards earlier, I must address the actual code style itself. The naming conventions, in particular, are rather peculiar. Variables follow a format that, in my opinion, significantly impacts code readability:
$quiz = null;
$errorstring = null;
$allowfilelocking = false;
$courseresults = [];
$camelCase = null; // Variables should not be in camelCase, PascalCase.
$error_string = null; // Variable names should not contain underscores.
The prohibition of both underscores and camelCase makes complex variable names particularly challenging to parse. While consistency in coding standards is valuable, this specific choice seems to prioritize historical convention over developer experience.
Old Codebase and Technical Debt
Like WordPress, Moodle carries the weight of its history in its codebase. Years of development under older PHP versions have left their mark, resulting in significant technical debt. However, there’s a silver lining: starting with version 4, Moodle has undertaken an ambitious refactoring project. Each subsequent release has brought more modularization, better namespacing, and modern PHP practices. They’re implementing PSR standards for hooks and enforcing namespaces in extensions. While these changes sparked considerable community discussion, especially around version 4, they demonstrate how an open-source project can methodically modernize its codebase.
This evolutionary rather than revolutionary approach means that many core plugins still use older code patterns and APIs. As a result, developers can’t always look to core code as an example of best practices. However, the gradual transformation shows a clear path forward while maintaining backward compatibility.
Documentation & Community Resources
The developer documentation currently exists in a somewhat fragmented state, split between new and old sites, with significant gaps in coverage. I often found myself diving into core plugin source code to understand implementation details, partly because in-code documentation and return type hints weren’t always accurate. While this experience mirrors my WordPress development workflow (where I frequently reference Gutenberg’s source code), the current state of Moodle’s documentation could be more welcoming to new developers.
Using the community forum, while running on Moodle itself, wasn’t the best experience: The search functionality is slow and often ineffective.
Minor Quirks
A few other things bugged me: Extensions need custom PHP files as entry points where you manually include Moodle’s bootstrap file – feels a bit old-school compared to WordPress and Laravel’s routing. Also, they’re still using Grunt for building assets too.
Core-updates are also a bit clunky since you need to handle complete directories in Git instead of using a package manager.
Takeaways
Building this Moodle extension turned out way more interesting than I expected. I’m not planning to leave WordPress development anytime soon – I rarely work on learning platforms anyway – but I learned a lot about different ways to tackle common development problems.
I really like how Moodle handles standardization, especially with their UI components and APIs. The way they structure extension development makes a lot of sense. And the fact that they include so much functionality in core, care about accessibility, and have clear rules for different extension types – there’s definitely something other platforms could learn from this.
Sure, Moodle shows its age here and there. Parts of the code are messy, some things feel old-school, and development isn’t always smooth sailing. But I’m impressed by how they’re modernizing everything step by step while keeping things working. Once you get used to their strict rules and structure (which felt limiting at first), development actually becomes easier.
This whole experience gave me more than just a finished client project – it changed how I think about platform architecture and extension development. Moodle might not be the shiniest or most modern system out there, but it has some really clever solutions that are worth learning from.
- This reflects my experience with professional WordPress projects, where certain functionality is repeatedly needed across sites. While WordPress’s minimal core approach makes sense for personal blogs (its original target audience), there’s a valid discussion to be had about what should be included in core for modern use cases. Moodle’s approach of shipping commonly-needed functionality demonstrates one possible solution to this challenge. ↩︎