Moodle plugin development

Moodle plugin development means choosing the right plugin type, building on core APIs, and testing and packaging the result so it still works after six-monthly releases and LTS jumps. This guide covers the decisions, build sequence, effort drivers and handover.

On this page

Moodle plugin development that survives major upgrades

When configuration cannot deliver the functionality a team needs, the decision is whether to build a plugin, contribute the change upstream to Moodle core, or modify core directly. For most organizations, a plugin is the right starting point because it keeps custom code separate from core and can be updated independently. Modifying core is the option to avoid, since every future upgrade then has to merge your changes into Moodle's own code. Upstream contribution is worth considering when the feature would help other sites, but it follows Moodle's review process and its timing belongs to the community, not to your roadmap.

Moodle ships major releases every six months, in April and October, and minor point releases on a fixed bimonthly schedule. Long-term support releases and other supported versions have different end dates, so a site may run one release while the business expects the plugin to keep working on the next. A plugin built against one release must therefore be kept compatible with later releases, and that compatibility has to be planned from the first commit. Before writing code, confirm which Moodle versions the client will run during the plugin's expected life, and check the current support dates on the official releases page rather than relying on memory or older documentation.

Several things make plugin development harder than it looks. Core APIs change between releases, and functions that a plugin calls can be deprecated, renamed or removed, so the plugin needs deliberate use of documented APIs and a regular review of deprecation notices. PHP and database floors also change with each release. Moodle 5.1 requires 64-bit PHP 8.2 or later and specific database versions, while Moodle 5.3 raises the PHP floor to 8.3 and requires newer database versions, so a plugin's own dependencies and the hosting stack must move together. From Moodle 5.1, the web server document root must point to the new /public directory, which affects how test and staging environments are built and can cause failures that look like plugin bugs.

Maintenance is the part most often underestimated. A plugin needs its own test suite, continuous checks against the Moodle versions in use, a packaging and release process, and an owner who reviews upstream changes before each upgrade. Ask any partner to show how the plugin will be tested on each supported Moodle release and PHP version, who will fix it when a core change breaks it, and how upgrade testing fits into the client's change calendar. These responsibilities continue after go-live, so they belong in the scope and in any support arrangement, not in a later conversation.

Which Moodle plugin type fits the job

Moodle groups custom code by plugin type, and each type has its own place in the code tree, its own entry points and its own upgrade expectations. Picking the type before writing code decides which core APIs you can use, how the plugin is installed and how much of it survives a major release.

Activity modules

An activity module adds a new kind of course activity, such as a quiz-like tool or a submission workflow. It lives under the mod directory and is the most involved type, because it needs its own database tables, capability definitions, backup and restore handlers, and grade integration. Check how it reports completion and events, and confirm that its backup and restore logic is tested against a course with content from the previous release.

Blocks

A block is a small panel placed in courses, the dashboard or the site front page, typically to show a summary, a link set or a notice. Blocks live under the blocks directory and render through the block API, so they inherit placement and visibility rules from Moodle. Check which contexts the block can be added to, and whether it reads data that a user should only see with a specific capability.

Local plugins

Local plugins are the general-purpose type for site-specific code that does not fit another type. They live under the local directory, can register pages, callbacks and web services, and are easy to start with. That flexibility is also the risk: a local plugin that duplicates the job of a block, report or admin tool is harder to upgrade and review, so check first whether an existing type covers the requirement.

Reports

Report plugins build views over course, user, grade or completion data. Their code lives under the report directory and typically reads through the reporting and capability checks, which keeps access rules consistent with the rest of Moodle. Confirm the data sources and the capabilities that gate each report, and test that a user without permission cannot reach the data through a direct URL or an export.

Enrolment plugins

Enrolment plugins decide how users join courses, for example from an external source, a payment step or a scheduled feed. They live under the enrol directory and manage enrolment instances, roles and end dates through the enrolment API. Confirm how the plugin handles unenrolment and role changes, because those cases drive most support questions after an integration goes live.

Admin tools and authentication plugins

Admin tools (under admin/tool) provide site administration utilities such as data cleanup, migration helpers or monitoring pages. Authentication plugins (under auth) control how users log in, and Moodle core already ships types such as LDAP, OAuth 2, Shibboleth and web services. Before writing an authentication plugin, check whether a core or maintained plugin already covers your identity protocol, since login code carries the highest security and upgrade exposure.

Build a plugin, configure, contribute or change core

This table compares the five realistic ways to meet a Moodle requirement, from configuring existing settings to changing core, along with the operational cost each one carries. Read each row as a decision: find the option that fits the requirement, then check the upgrade, security and testing costs before committing. The main takeaway is that the further a change sits from Moodle's supported APIs, the more of its ongoing cost you carry yourself.

OptionWhen it fitsUpgrade and ownership costTesting and third-party dependency
Configure existing settingsThe need is met by core features, roles, capabilities, categories or course formats already shippedLowest. Settings survive upgrades, but review them after each major release for changed defaultsStandard regression testing of your course designs and roles. No third party in the chain
Use an existing contributed pluginA maintained plugin covers the requirement and its supported Moodle versions include your target releaseLow to moderate. You track the plugin's release cycle and test it against each Moodle upgradeDepends on the plugin maintainer's release pace. Confirm supported Moodle versions in the plugins directory before adopting
New custom plugin of a specific typeNo suitable plugin exists and the need fits a documented plugin type such as activity, block, local, report or enrolmentModerate to high. You own the code, its security fixes and its compatibility with each release that followsYou write and maintain the test suite. Dependence is on Moodle's plugin APIs, which change between releases
Fork a contributed pluginAn existing plugin is close to the need but cannot be extended through its settings or hooksHigh. You carry every upstream fix forward by hand and decide which security patches to mergeFull retest on every upstream change you choose to take. Your fork depends on your own team, not the original maintainer
Change Moodle coreOnly a change inside core can meet the requirement, and no plugin hook or API exposes the point you needHighest. Core changes can conflict with every upgrade and must be reapplied or rewritten each releaseFull regression across the site. Core edits are harder to isolate, so the test scope grows with every change
Contribute upstream to core or a maintained pluginThe change is generally useful and you want it maintained by the project rather than by your teamLowest long-term burden once merged, but timing depends on review, not on your scheduleMoodle HQ or the plugin maintainer reviews, tests and merges the code. Expect review rounds and possible rework to match their standards

Moodle plugin development sequence from scaffold to package

This sequence takes a custom Moodle plugin from a confirmed plugin type to a tested release zip that installs cleanly and upgrades safely across Moodle versions. Each step closes with a check the reader can verify before moving on.

  1. Confirm plugin type and component name

    Before writing code, the team confirms which plugin type fits the requirement, such as activity module, block, local, report or enrolment, using the Moodle plugin types documentation. The component name follows the type's frame, and the choice determines where the code lives and which APIs apply. Close the step by writing a one-page design note that names the type, the component and the core APIs it will call. A reviewer from the platform team should sign it off before scaffolding starts.

  2. Scaffold standard plugin files and metadata

    The developer creates the standard file set for the chosen type, including the version metadata file with the plugin version, component name, release label and the declared minimum Moodle version. The minimum version should match the oldest Moodle release the client runs, and the team should confirm it against the release notes for each target version. The step closes when the plugin is placed in the correct directory of a test Moodle install and the site lists it under Plugins overview without errors.

  3. Define the data model through install and upgrade

    Database tables are declared in the install file and changed only through the upgrade mechanism, with each schema change tied to a new plugin version number. Ad hoc schema changes on a test site make later upgrades unreliable, so every change must go through versioned upgrade steps. The check is twofold: a clean install from an empty database creates the expected tables, and an upgrade from the previous plugin version reaches the same schema without manual intervention.

  4. Implement capabilities and access checks

    The developer defines capabilities in the access file, assigns default roles, and checks them in every page, service and external function that exposes data or actions. Access decisions rely on context and capability checks rather than on role names or hidden URLs. The step closes when a test matrix covering at least an administrator, a teacher, a student and a user without enrolment shows the expected allow and deny result for each action.

  5. Build pages with core output and form APIs

    Pages are built with Moodle's output renderers, templates and the core form API rather than raw HTML or direct request handling, which keeps the plugin aligned with theme changes and core security handling. Forms validate input server side and use the sesskey and form token checks that the API provides. The step closes when each page renders in the default theme, submits its forms with valid and invalid input, and rejects a request that lacks a valid session token.

  6. Hook events and privacy declarations

    Where the plugin stores user data, the team declares a privacy provider that describes the data stored, exports it on request and deletes it when required. Significant actions raise events so that logs, reports and other plugins can react without polling the plugin's tables. The check is a privacy export and deletion run for a test user, plus a log report showing the expected events for create, update and delete actions.

  7. Write language strings and add automated tests

    All user-facing text goes into the language file with descriptive keys, so translators and site administrators can change wording without code edits. The team adds PHPUnit tests for the data model, capability checks and key functions, and behat tests for the main user journeys where the feature warrants them. The step closes when the test suite passes on a fresh install of each Moodle version and database the client supports, and the language file contains no hard-coded strings in the templates.

  8. Run the code checker and static analysis

    The developer runs the Moodle code checker and the available static analysis tools against the plugin, then fixes every reported error and reviews warnings for real defects. Results are recorded in the release notes or review log so that the client's technical team can see what was checked. The step closes when the checker and analysis report no errors, and each remaining warning has a written reason, such as an accepted false positive.

  9. Package a signed release zip with install notes

    The final step builds a release zip whose top-level folder matches the component name, with the version metadata, licence file and install notes that list the minimum Moodle version, database requirements and any post-install configuration. The package is signed or checksummed according to the client's release policy, and the team confirms the checksum after transfer. It closes when the zip installs cleanly on a fresh test site and upgrades from the previous release on a copy of production data.

Using core APIs so plugins keep working after upgrades

Most Moodle plugins that break at a major upgrade break for the same reasons: they reach into private core functions, write raw SQL that bypasses the database abstraction layer, copy core templates into their own code, or depend on behavior that a later release removes or changes. Each of these shortcuts works on the day it is written and then fails when core moves underneath it. The practical goal for a plugin developer is to rely only on documented, public interfaces so that the plugin's contract with core is explicit and can be checked at every upgrade.

The safer pattern is to build on the APIs Moodle documents for this purpose. That means the database access layer for all reads and writes, output renderers and templates for anything displayed, the form API for input handling, the access and capability API for permission checks, the events system for reacting to or publishing changes, the task API for scheduled and adhoc work, and the web service framework for any external integration. Each area has its own public entry points. Confirm exact class, function and method names against the developer documentation for your target version before you write code, because names and signatures are the details most likely to shift between releases.

Deprecation notices are the earliest warning you will get. For every target version, read the release notes and the developer changelog sections that cover the core areas your plugin touches, and record each deprecated call, removed API or changed behavior in a compatibility log kept with the code. Treat a deprecation as a task with a version attached: the deprecated call still works until the release that removes it, so the cost of fixing it is lowest while the old version is still in service. Also check whether a plugin you depend on has its own notices, since a plugin can break even when core is stable.

Avoid editing core files. A patched core file is overwritten by the next upgrade, silently removes your change, and makes every future core update a manual merge. If core lacks a hook you need, use the documented extension points, such as callbacks, events or plugin-type interfaces, or raise the gap with Moodle or your partner before building a workaround. The same rule applies to templates: override them only through the supported theme or plugin mechanisms, not by copying core templates into your code and drifting from them.

Theme and layout dependencies need separate attention. Moodle 5.3 removes the Classic theme from core, so any plugin, custom layout or theme child that depends on Classic templates, regions or styles must be reassessed before you move to that release. Confirm this against the Moodle 5.3 release notes and check your own code for references to Classic. Plan the reassessment as part of the upgrade scope, not as a late fix, because layout breakage is often visible only in the browser and needs manual testing across roles.

What drives Moodle plugin development effort and cost

This table lists the factors that most change the effort of a custom Moodle plugin and the steps that keep each one contained. Read each row as a question to settle in the written scope before work starts. The main takeaway is that narrowing supported versions, data model and integration count early does more to control cost than any later change to the build.

DriverWhy it changes effortHow to keep it downWhat to ask for in a fixed-scope quote
Number of target Moodle versionsEach supported release needs its own compatibility testing, and the 5.1 and 5.3 releases differ in PHP, database and document root requirements.Name the exact target versions, for example 4.5 LTS and 5.3 LTS, and drop any version outside the agreed list.A named version list, test matrix per version and the support window the plugin must cover.
PHP and database combinationsMoodle 5.3 requires PHP 8.3 or later and PostgreSQL 17, MySQL 8.4, MariaDB 11.4 or SQL Server 2019 or later, so every combination adds test runs.Agree a single primary database and the minimum PHP version, and test the others only where clients actually run them.The database and PHP versions covered, and which combinations are tested and which are only reviewed.
New database tables versus existing dataNew tables need install and upgrade scripts, data migration logic and uninstall handling, which add work and risk on every core upgrade.Reuse core tables, user preferences or custom profile fields where they meet the need.A data model description, install and upgrade script scope, and the uninstall behavior agreed in writing.
Number of integrationsWeb services, SSO, HR or student information system feeds and external APIs each need credentials, error handling, retries and test doubles.Limit each integration to the fields and events the business needs, and move bulk syncs to existing enrolment or import tools.A list of each integration with direction, frequency, data fields and the test system used for each.
UI complexityCustom pages, forms, JavaScript and templates take longer to build and test across browsers and screen sizes than standard settings pages.Use core forms, Mustache templates and core output renderers, and keep custom JavaScript to a minimum.Screens or pages in scope, supported browsers and devices, and the design source that acceptance will be checked against.
Grades and reporting logicCalculations that feed the gradebook or reports must match core grade handling, and edge cases such as hidden grades and overrides multiply test cases.Use core grade items and the reporting APIs, and define the calculation rules in a written specification before coding.A worked specification of each calculation, the grade edge cases covered and the reports' sample data.
Accessibility requirementsKeyboard navigation, screen reader labels and contrast checks add review and remediation time, especially on custom screens.Build on core components that already meet accessibility conventions and state the standard to test against.The accessibility standard and level targeted, the testing tools used and who signs off the results.
Privacy and data retentionPlugins that store personal data must implement the core privacy API, including export and deletion, and any retention rule needs its own logic and tests.Store the minimum personal data, and document retention periods before the design starts.Privacy API implementation scope, retention rules in writing and the data subject request tests to be delivered.
Test environment matching productionTesting against a site that differs from production in PHP version, cache setup, cron or data volume hides defects until go-live.Agree early on a staging copy of the production configuration, with representative data and the same cron and cache setup.Who provides the test environment, how it is built, and the acceptance tests the client signs off before release.

Securing custom plugin code and its data paths

A Moodle plugin runs with the same privileges as core code, so a flaw in a small plugin can expose the whole site, its gradebook and its users. The cards below cover the checks that matter most for custom code, from access control and input handling to credentials, personal data, bundled libraries and the review gate before production.

Capability checks on every entry point

Every page, form action and web service function in the plugin should verify the user's capability in the right context before it reads or changes data. Confirm checks run server side, not only hidden menu items or course-level settings, and that web service functions declare the capabilities they require. Review each entry point against the roles that should and should not reach it, including teachers in one course and managers at category level.

Input validation and output escaping

Read request data through Moodle's parameter types and the optional_param and required_param helpers rather than raw superglobals, so values are cleaned to the expected type. Escape output with the core rendering and formatting functions and templates, which apply context-appropriate escaping. Check every place user text is echoed, including emails, notifications, reports and exported files, not just the main page.

No execution of user-supplied content

Custom plugins sometimes store code, templates, formulas or expressions written by teachers or students and evaluate them. Avoid eval, dynamic includes and shell calls built from user input. If a feature must support expressions, use a restricted parser with an allowlist of operations and test it with hostile input. Treat any content that a user can edit as untrusted when it is rendered or processed later.

File uploads through the file API

Store uploaded files through Moodle's File API with a file area, a component and a context, rather than writing to dataroot or web-accessible folders yourself. Restrict accepted types and sizes in the form definition and in the server-side handler, since client-side checks can be bypassed. Confirm that downloads are served through the pluginfile mechanism so the capability checks on the context still apply.

External credentials in admin settings

API keys and passwords for external systems should be entered in the plugin's admin settings, stored through the mechanism the site already uses for secrets, and never committed in code, templates or default configuration files. Ask the site's administrators how they supply secrets in your environment and confirm that the settings page masks stored values. Check logs and debug output so credentials are not written there.

Minimal personal data and privacy API declaration

Store only the personal data the feature needs, and remove it when it is no longer required. Declare every table and field that holds personal data through the Moodle privacy API, including providers that export and delete data on request. Test the export and deletion paths with a sample user, because undeclared data is not included in subject access or erasure requests and creates compliance gaps.

Third-party libraries bundled in the plugin

Each library shipped inside the plugin becomes code your team must maintain. Record its name, version, license and source, check it against known vulnerability databases, and prefer core-provided libraries where Moodle already includes them. Assign an owner to watch the upstream security advisories and plan updates through your normal release cycle, since a bundled copy will not receive fixes automatically.

Pre-production review of the plugin

Before installing a plugin on production, review the code against the checks above, run it on a staging copy of the same Moodle version, and test with each role that will use it. Confirm the plugin's declared version, dependencies and compatibility with the target release. Ask the developer or your review partner for written results, and keep the reviewed version under source control so you can show exactly what was installed.

How to test Moodle plugin development before release

A Moodle plugin should not reach a client site until it has passed three layers of testing: automated unit and integration tests, behavior tests that follow real user journeys, and a clean install on every Moodle version the site will run. Start with the Moodle test framework. Unit tests cover the plugin's own logic, while integration tests cover its calls into core APIs such as the database layer, capability checks, events and the file API. Keep the test suite in the plugin repository so it runs in continuous integration on each commit. Then run behavior tests for the journeys that matter to the client, such as a teacher creating and configuring your activity, a student completing it, and a manager reviewing the resulting data. Record which journeys were tested and with which role, because a missing role check is one of the most common defects these tests expose.

Test the install itself on each target combination. The plugin should be installed from zero on every Moodle version the site will use, with the PHP and database versions that version requires. For example, the Moodle 5.1 release notes require 64-bit PHP 8.2 or later, and list PostgreSQL 15, MySQL 8.4, MariaDB 10.11 or Microsoft SQL Server 2017 or later as supported databases. Confirm the exact requirements for your target release with the Moodle release notes before you build the matrix. Test environments must match the PHP and database versions the production site will run, not the newest versions available. A plugin that passes on PHP 8.4 and fails on PHP 8.2 is not ready for a site that stays on 8.2. Also reproduce the web server configuration. From Moodle 5.1, the document root must point to the /public directory, and a test server that serves the main Moodle folder can hide errors that production will show, or expose directory listings.

The upgrade test is where most data-loss risk sits. Install the previous version of the plugin on a copy of a production-like site, populate it with realistic course and user data, and then upgrade to the new version. Use a copy of production where you can, and keep the database and moodledata backup from before the upgrade so the test can be repeated. After the upgrade, confirm that existing records are intact and that the upgraded data still displays and reports correctly. Then check that scheduled tasks and web services still run: trigger the plugin's tasks manually, confirm they complete, and call each web service function with a token that has the right capabilities. Repeat the upgrade from every version still in use on live sites, not only the immediate predecessor.

Failure testing shows what happens when things go wrong, and it is where plugins most often fail in production. Test a user who lacks the required capability, and confirm the plugin denies access with a clear message rather than a fatal error. Disable optional services the plugin depends on, such as a message output or an external web service, and confirm the rest of the activity still works. Submit bad input, including oversized values, unexpected characters and malformed uploads, and confirm validation rejects them. Most importantly, take the external system offline, whether it is an API, an LTI tool or a database connection, and record exactly what learners and administrators see. The plugin should show a known, documented state and should not hang the page or block course access. Agree the expected behavior for each failure with the client before testing starts, so the result can be judged against a specification rather than opinion.

A test passes when every unit and integration test is green on each target version and database combination, every defined user journey completes for each role, the upgrade test preserves all data and leaves tasks and web services running, and every failure case produces the agreed behavior. Keep the evidence: the test suite version and commit, the PHP and database versions used for each run, the Moodle version and build number, the upgrade logs, screenshots or recordings of the journeys, and the failure-case results with the observed messages. Store these with the release record so the client can repeat the checks after the next Moodle release. Ask your developer or partner to confirm in writing which versions and databases are covered, and which combinations were not tested, so that gaps are visible before go-live.

Questions teams ask before building a Moodle plugin

Can our own developers build a Moodle plugin in-house?

Yes, if you have developers who know PHP, Moodle's core APIs and the plugin type you need. Moodle is GPL-3.0 licensed, so you can build and modify plugins without a license fee, and Moodle documents more than 50 plugin types, including activity modules, blocks, local, report and enrolment plugins. The harder question is whether your team can keep the plugin working through upgrades. Each Moodle major release, and each point release, can change the APIs your code depends on. Before deciding, ask whether your team has someone who will own code review, automated testing and an upgrade check for every release cycle, not just the first build. If that capacity is not there, a partner can build it, but the ongoing work still needs an owner on your side.

What drives the cost of a Moodle plugin and what should a quote include?

Cost is driven mainly by the plugin type, the number of integrations it touches, the volume of business rules, the reporting or data it must handle, and how much testing it needs. A simple block differs greatly from an enrolment plugin that syncs from an HR system. A fixed-scope quote should list the plugin type and its user stories, the Moodle versions it must support, the external systems it connects to, the test approach, the documentation and packaging deliverables, and the handover terms. It should also state what you must provide, such as sample data, staging access and named reviewers. Ask the vendor to separate one-time build work from recurring upgrade and support work, since the recurring part is often what determines long-term cost. Confirm any figures in writing against your own scope.

How long does Moodle plugin development usually take?

Duration depends on scope, not on a standard timeline. The main factors are the plugin type, how many external systems it integrates with, how precisely the requirements are defined, how many Moodle versions it must run on, and how quickly your reviewers can approve work. Requirements that change mid-build add time more than almost any technical factor. Ask the vendor for a phased plan with milestones for design, build, testing and handover, and ask which milestones depend on your input. If you need a specific date, for example before an academic term, ask for the schedule to be tied to a dated acceptance point and to the Moodle release calendar, so the work does not land on a version that is about to change.

What does the work require from our team?

Plan for four inputs. First, a test environment that matches your production Moodle version, PHP version and database, so results are meaningful. Second, subject matter experts who can explain the business rules and confirm behavior for teachers, students or administrators. Third, written acceptance criteria agreed before build starts, so reviews have a clear standard. Fourth, access to staging and a named person who can approve releases. Also decide who holds the source repository and who receives credentials. Delays usually come from slow reviews, unclear rules or staging environments that differ from production. Ask the vendor to list every dependency on your side in the quote, with the person responsible and the expected response time.

Should we contribute our Moodle plugin upstream?

Contributing upstream is a decision about licensing, maintenance and visibility, not a requirement. Because Moodle core and most plugins are GPL-3.0 licensed, you can publish a plugin publicly, and sharing it can lower long-term upkeep when others review and maintain it. Publishing also means accepting public review, meeting the community's coding standards and responding to issues. A plugin that encodes local policy, contains integration secrets or depends on private systems is usually better kept private. Ask your partner which parts can be separated from local configuration, and confirm with your legal and security teams whether any data, credentials or institutional names must be removed before publication.

How is a Moodle plugin kept working after each release?

A plugin stays working through a combination of version declarations, automated tests, and a regular upgrade check. Moodle ships major releases every six months, in April and October, and minor point releases every two months, so the cadence is frequent. Each plugin declares the Moodle versions it supports, and each change to a core API can break it. Ask for a plan that runs the plugin's test suite against each new supported Moodle version in a staging copy of your environment before production upgrades, and that records a named owner for fixes. Also ask how security patches to the plugin will be handled and how long the support window lasts. Confirm the support terms in the written scope.

Who owns the plugin code after handover?

Ownership is set by the contract, so it must be written down before work begins. Typically the client owns the custom code and its documentation, while any third-party libraries keep their own licenses. Confirm that the agreement covers source code, build and packaging scripts, test suites, documentation, and the right to modify or hand the plugin to another provider. Ask who keeps the repository, who holds deployment credentials, and what knowledge transfer is included. Check the license of every dependency, because GPL obligations can apply if a plugin distributes Moodle-derived code. Your legal team should review these terms with the written scope.

What to hand over and maintain after a plugin ships

A plugin is not finished when the code merges. It is finished when the organization that will run it can install it, upgrade it, test it and explain what it does with data, without calling the developer. The handover package is the set of artifacts that makes this possible. Ask for the source repository with its full commit history, so later changes can be traced to a reason. Ask for an installable release zip that matches a tagged commit, a version and compatibility statement naming the Moodle releases the plugin was tested against, and install and upgrade notes that cover the exact steps for a fresh install and for an upgrade from the previous plugin version.

The rest of the package covers what the operators need to act on. Admin documentation should describe every setting, capability and scheduled task the plugin adds, with the role that should hold each capability. The automated test suite must be delivered with instructions for running it against a clean Moodle install, including which PHPUnit and Behat configuration it expects. Privacy and data handling notes should list each table and file area that stores personal data, the Privacy API provider implementation, and any data sent to external services. Known limitations should be written down rather than discovered in production. Finally, request a dependency list with the license of each bundled library, since GPL-3.0 compatibility and third-party licenses must be reviewed by the client, not assumed.

After go-live, four activities need a named owner. First, someone must read Moodle release notes and deprecation lists for every new supported version, because core API changes are what break plugins during major upgrades. Second, the automated test suite must be rerun on each new supported Moodle release, and the results kept as evidence. Third, bundled libraries need tracking against security advisories, using the library's own advisory feed or a dependency scanner the client already runs. Fourth, scheduled tasks and error logs should be checked after each release and during exam periods, since a failing task often shows up first as missing data rather than as an error page.

Ownership usually splits between the client's Moodle administrator, who watches releases, runs the test suite in a staging site and handles upgrades, and the client's security or compliance lead, who reviews advisories and the privacy notes. The developer's role is the fix and the retest. If the work is later supported under a retainer, ask for the response scope in writing: which Moodle versions are covered, how deprecation reviews are reported, how many test reruns are included per release, who triages advisories, and how urgent fixes are prioritized against new features. Confirm whether the retainer covers the Moodle release cycle or only defects. Those answers decide whether the plugin keeps working after the next upgrade.

How DeployOpen scopes, builds and hands over Moodle plugins

DeployOpen plans, builds, tests, documents and hands over Moodle plugins inside your own infrastructure, so the code runs where your team can operate it. Each engagement starts from a written, fixed scope that names the plugin type, the Moodle versions it must support and the deliverables you receive.

How we scope

  • We confirm the plugin type first, such as activity, block, local, report or enrolment, because it determines which core APIs and extension points apply.
  • We record the Moodle versions and PHP and database versions the plugin must support, so compatibility is agreed before any code is written.
  • The written scope lists the features, integrations, test coverage, documentation and packaging you will receive, and it is agreed before work starts.

How we build

  • We build on Moodle core APIs and standard plugin structures rather than patching core files, which keeps the plugin upgradeable across releases.
  • Each change goes through code review against Moodle coding guidelines, with security checks on input handling, capabilities and file access.
  • We test the plugin on a staging copy of your site using the Moodle test suite and your own sample courses, users and roles before release.

How we hand over

  • You receive the source code in a version-controlled repository, with a package that installs through the standard Moodle plugin process.
  • Documentation covers installation, configuration, required capabilities, upgrade steps and known limits, so your administrators can run it without us.
  • We walk your team through the upgrade path for the next Moodle major release, and confirm which tests must pass before you move the plugin forward.

How we support

  • Ongoing maintenance, compatibility checks against new Moodle releases and security fixes can be covered by a retainer agreed in writing.
  • Before each Moodle upgrade, we check the plugin against the new release notes and flag any removed APIs or changed database requirements.
  • Confirm with us during scoping which support level, response expectations and retainer terms apply, so ownership of future changes is clear from day one.

Sources and further reading

Talk to our team.

Tell us what you're working on, whether it's a deployment, an audit, a security test or a cyber range. You'll speak with an engineer who can help you scope it.

  • 30-minute call: free, with no obligation.
  • NDA on request: we can sign before you share details.
  • Clear next steps: a scope and plan after the call.