Ldesign Media
    Back to Blog
    Development

    Why Choose Custom Moodle™ Plugins?

    Luuk Verhoeven

    Luuk Verhoeven

    Lead Developer & Moodle™ Expert

    April 1, 2026
    10 min read
    Why Choose Custom Moodle™ Plugins?

    When you need to do something it doesn't do out of the box, the instinct is to search the directory. There are nearly 2,800 listed there, surely someone has already solved your problem. And sometimes they have. But after building more than 300 custom over fifteen years, we've seen the full cost of relying on community for enterprise-grade requirements, and it's rarely as cheap as it first appears.

    This isn't an argument against open-source community . Many are excellent, and we use them ourselves. This is an argument for being deliberate about when custom development is the right call, and understanding what you're actually getting when you commission a built specifically for your organization.

    The Hidden Costs of Off-the-Shelf Plugins

    Version Compatibility: The Upgrade Tax

    releases a major version roughly every six months. Each release cycle, organizations running community face what we've come to call the upgrade tax: the time and risk associated with checking whether every installed has been updated for the new version, testing that claim compatibility but behave differently against your data, and dealing with whose maintainers have moved on.

    We've audited installations for clients, KLM, Nedap, BSL Media & Learning, where between 10 and 30 percent of installed were either unmaintained or running on versions two or three major releases behind. Those don't just cause technical debt. They become the reason upgrades get postponed, which compounds into larger and more disruptive migrations later.

    A custom built to your organization's specifications is maintained by a party with a direct contractual interest in keeping it working. When 5.x introduces breaking changes, we update our clients' as part of the engagement, not as an afterthought if the original developer happens to be available.

    Performance Bloat

    Community are built to serve many organizations with different needs. This means they carry configuration options you'll never use, database tables storing data you don't need, and event listeners firing on every page load regardless of whether that feature is relevant to your context.

    We've profiled instances where a single community activity module was responsible for 30 to 40 percent of total database query time, because it was issuing broad queries optimized for flexibility rather than for the specific access pattern of the host organization.

    When we build a custom activity module or report , we design the schema around the actual queries it will run. Indexes are placed where they'll be used. Caching is implemented for the specific data that's expensive to compute. The result isn't just faster, it's predictably fast, because the isn't doing anything it doesn't need to do.

    Security Risks You Can't Control

    The security team does excellent work, but they cannot review the entire directory continuously. Community vary enormously in their approach to security: some follow 's security guidelines meticulously, others were written quickly to solve a specific problem and haven't been revisited since.

    We've conducted more than 50 security audits of installations for Dutch organizations, and the findings follow a pattern. The core installation is usually in reasonable shape. The are where the vulnerabilities accumulate, SQL injection through unsanitized parameters, cross-site scripting through unescaped output, access control checks that can be bypassed with a crafted URL.

    With a custom , you control the codebase. We apply 's full security , required_capability(), $DB->get_records() with parameterized queries, clean_param() on every external input, proper session key validation on form submissions. And because we wrote it, we know exactly where to look when a security review surfaces a concern.

    The Support Gap

    When a community breaks during a upgrade, your options are limited. You can wait for the maintainer to release a fix. You can fork the and fix it yourself, taking on maintenance responsibility. Or you can pay a developer, possibly us, to dig into a codebase we didn't write and don't know intimately.

    None of these are good options when the in question handles critical functionality: course completions feeding into HR systems, certification tracking for regulated industries, or the custom enrollment workflow that 15,000 users interact with every day.

    What Custom Plugin Development Actually Looks Like

    The process of building a custom is more structured than people sometimes expect. It's not a matter of writing some PHP and dropping it into the directory. has a well-defined , and working within it correctly is what makes maintainable, upgradeable, and secure.

    Phase 1: Requirements and Architecture

    We start every project with a requirements session where we push back on vague specifications. "We need a custom certificate " is a starting point, not a spec. We need to know: what data goes on the certificate, who can generate it, what triggers issuance, where the PDFs are stored, how certificates are verified, what happens when a course is updated after a certificate is issued.

    These conversations surface constraints that change the architecture. A certificate for a healthcare training provider (like Dentallect) has different requirements than one for a corporate training platform, regulated professions may require certificates to reference specific qualification frameworks, include verifiable identifiers, and survive the organization's installation being replaced.

    The output of this phase is a technical specification that identifies the type (activity module, block, local , auth , format, report, admin tool), the database schema, the capability definitions, and the integration points with core.

    Phase 2: Plugin Structure

    Every starts with a version.php that tells who made it, what version it is, and what version of it requires:

    <?php
    defined('MOODLE_INTERNAL') || die();
    
    $plugin->component = 'mod_clientactivity';
    $plugin->version   = 2026021500;
    $plugin->requires  = 2024042200; // Moodle 4.4
    $plugin->maturity  = MATURITY_STABLE;
    $plugin->release   = '1.2.0';

    The version number follows the date convention uses: YYYYMMDDXX. Every time we release an update, this number increments, which tells 's upgrade system that database changes need to be applied.

    For an activity module, lib.php defines the mandatory functions that calls to integrate the activity into the course flow:

    <?php
    function clientactivity_add_instance($moduleinstance, $mform = null): int {
        global $DB;
        $moduleinstance->timecreated = time();
        $moduleinstance->timemodified = time();
        return $DB->insert_record('clientactivity', $moduleinstance);
    }
    
    function clientactivity_supports(int $feature): ?bool {
        return match ($feature) {
            FEATURE_MOD_INTRO           => true,
            FEATURE_COMPLETION_TRACKS_VIEWS => true,
            FEATURE_GRADE_HAS_GRADE     => true,
            FEATURE_BACKUP_MOODLE2      => true,
            default                     => null,
        };
    }

    The clientactivity_supports() function is what tells which platform features this activity works with, completion tracking, grading, backup/restore. Getting this right matters: if you claim FEATURE_BACKUP_MOODLE2 support without implementing the backup properly, courses containing your activity will produce corrupt backup files.

    Phase 3: Database and Upgrades

    The db/install.xml file defines the initial schema using 's XMLDB format. When the is first installed, 's upgrade system creates these tables automatically. When we release an update that changes the schema, we add a step to db/upgrade.php:

    <?php
    function xmldb_clientactivity_upgrade(int $oldversion): bool {
        global $DB;
        $dbman = $DB->get_manager();
    
        if ($oldversion < 2026021500) {
            $table = new xmldb_table('clientactivity');
            $field = new xmldb_field('externalref', XMLDB_TYPE_CHAR, '255', null, null, null, null, 'timemodified');
    
            if (!$dbman->field_exists($table, $field)) {
                $dbman->add_field($table, $field);
            }
    
            upgrade_mod_savepoint(true, 2026021500, 'clientactivity');
        }
    
        return true;
    }

    This is how schema changes survive upgrades without data loss. The version check ensures each migration only runs once, even if an administrator runs the upgrade process multiple times.

    Phase 4: Testing

    We test at multiple levels depending on the project's scope and budget. Sometimes clients request unit tests, where PHP classes that implement the 's core functionality are tested with PHPUnit, with 's test data generator providing realistic fixture data. In other cases the budget is limited or the is straightforward enough that unit tests don't add proportional value, so we focus testing effort where it matters most. Integration tests run against a real database to verify that the installs cleanly, that the database schema is created correctly, and that core APIs interact with the as expected. And before any goes to production, we do manual acceptance testing against a staging instance that mirrors the client's production configuration.

    We also run 's built-in code checker tools: phpcbf for code style, phplint for syntax errors, and 's own moodlecheck for compliance. The community directory requires these checks to pass, but for custom , we apply them because they catch real problems.

    All of this is tied together with CI pipelines. Every commit triggers automated checks: code style, linting, and any configured tests run automatically before code can be merged. For deployment, we use CI/CD pipelines that handle staging and production releases, so updates reach the right environment without manual intervention or human error.

    Types of Plugins We Build

    Activity Modules

    The most complex type. Activity modules appear in the course section list alongside 's built-in activities (Assignment, Quiz, Forum). Real examples from our portfolio include:

    • KLM Travel Journal for cabin crew training, where crew members document real-world experiences as structured learning activities
    • Open Webinar for live and recorded webinar sessions with attendance tracking and completion integration
    • Custom certificates with automated generation based on course completion and configurable templates
    • Self-assessment modules and 360-degree feedback tools for professional development programs
    • Time registration activities for tracking study hours in regulated training environments
    • Canvas-based interactive activities for creative and visual learning assignments

    Authentication and Enrollment Plugins

    Auth control how users authenticate. Enrol control how users get into courses. These two areas are where enterprise system integration happens.

    We have built dozens of authentication integrations for organizations like KLM, AFAS, SURFconext, Fontys, and UWV. These range from SAML2 and OpenID Connect (OIDC) implementations to fully custom solutions. Each handles organization-specific attribute mapping: taking attributes from the identity provider and using them to populate profile fields, assign cohorts, or trigger enrollment in specific course categories. For HR-system integrations (SAP, AFAS, Workday), we have built enrollment sync that poll an on a schedule, create users who have arrived, suspend users who have left, and update profile data in between. We also build enrollment for payment integrations (Mollie, course payment gateways) and voucher-based access.

    Reports and Analytics

    's built-in reporting is adequate for simple use cases. For enterprise clients who need to answer questions like "which of our 8,000 employees completed the mandatory safety training this quarter, broken down by department and employment type," the built-in tools reach their limits quickly.

    We build report using 's report , which integrates them into the administration and course-level navigation. Examples include certificate reports, coach view completion dashboards, group progress overviews, and attempt analytics. These reports can join completion data with profile fields, custom user info fields, and cohort membership to produce the exact views that HR and compliance teams need.

    Course Formats

    Course formats control how a course's section list is presented to students. Beyond the built-in Topics, Weeks, and Social formats, we have built:

    • VSF (Vertical Section Format) for clean, scrollable course layouts
    • Tab-based formats like TabTiles for VUmc, organizing sections into navigable tabs
    • Grid formats customized for KLM, UWV, and other enterprise clients
    • ANWB Streetwise format for primary school traffic safety education
    • Game dashboard formats with interactive progression elements

    Blocks

    Blocks are sidebar components that can be added to any page in . We use them for dashboard widgets, notification systems, quick-access menus, and embedding content from external systems. Real examples include voucher management blocks, recertification tracking, user avatar (webcam snapshot) blocks, course note widgets, Streetwise navigation for ANWB, and custom dashboard blocks for various enterprise clients. We have also built Commander, a quick-navigation block that lets administrators and teachers find pages instantly.

    Themes

    's theme system controls the entire front-end presentation. For enterprise clients, this usually means a custom theme built on 's Boost base theme, implementing the organization's design system, adjusting navigation structure, and ensuring accessibility compliance. We have built themes for Nedap, De Schoolschrijver, Vakmedia, Citaverde, and others, each matching strict brand guidelines.

    Availability Conditions and Local Plugins

    Beyond the main types, we build availability conditions that control access to activities based on criteria like IP address restrictions, voucher codes, or payment status. Our local handle everything from GDPR compliance tools and OneRoster data synchronization to CSV user imports and quiz monitoring.

    The Cost-Benefit Analysis: When Does Custom Make Sense?

    Community make sense when the functionality they provide closely matches what you need, when they're actively maintained, and when their security posture is acceptable for your environment.

    Custom development makes sense when:

    • The community plugin exists but doesn't fit. If you need to modify a community significantly, you've effectively forked it, and now you're responsible for maintaining a diverged codebase through every upgrade. It's often cheaper to build what you actually need.
    • The functionality involves sensitive data or access control. Enrollment logic, grade calculations, and certificate issuance are areas where a bug has real consequences. These are not places to accept code you can't fully audit.
    • The plugin needs to integrate with your systems. Community are built for a general audience. A that syncs with your specific HR system, using your specific authentication scheme and your specific data model, needs to be written for your situation.
    • You're operating at scale. A that performs acceptably for 500 users may not perform acceptably for 50,000. Custom can be optimized for your specific load profile and access patterns.
    • You need guaranteed support continuity. For functionality that's critical to your operations, you need to know that someone with intimate knowledge of the code is available when something goes wrong.

    How to Evaluate Whether You Need a Custom Plugin

    Before commissioning custom development, work through these questions:

    Does a community exist that covers 80% or more of your requirements without modification?

    If yes, evaluate whether the remaining 20% is truly necessary or whether you can adapt your process.

    Is the community actively maintained?

    Check the last update date, the number of open issues, and whether the maintainer has responded to recent bug reports. A last updated for 3.9 is effectively unmaintained.

    Have you read the community 's code?

    For any handling authentication, enrollment, grades, or financial transactions, you should review the code or have someone review it for you. The directory does not guarantee security.

    What's the cost of the failing?

    If the answer is "we lose compliance records" or "enrollment for 10,000 users breaks," the calculus shifts toward custom development with guaranteed maintenance.

    Does the need to survive your organization across multiple versions?

    Longevity requirements favor custom development with a maintenance agreement.

    Realistic Expectations

    Custom development takes longer and costs more upfront than installing a community . That's a real difference, and it matters for organizations with tight timelines or limited budgets.

    What it buys you is a that does exactly what you need, performs well at your scale, can be audited and maintained by people who know the code, and won't hold up your upgrade because its maintainer disappeared.

    For Ldesign Media's clients, organizations like KLM running training for thousands of employees, or Yuverta managing student progression across a network of MBO schools, that reliability is the point. The training platform is infrastructure, and infrastructure needs to be dependable.

    The 300+ we've built since 2010 range from small utility that solve one specific problem, to complex multi-component systems that sit at the center of a client's entire learning operation. The common thread isn't complexity, it's that in every case, the does what the organization actually needs, and it keeps working when updates.

    Luuk Verhoeven

    Written by

    Luuk Verhoeven

    Lead Developer & Moodle™ Expert at Ldesign Media

    Software engineer and architect specializing in Moodle™ plugin development, system integrations, and scalable LMS platforms. Building custom solutions since 2010.

    Free: the Moodle™ Upgrade Checklist

    The 26-step checklist we use for client upgrades: from security and end-of-life check to staging tests, go-live, and aftercare.

    • Avoid the most common upgrade pitfalls
    • Covers plugins, integrations, and rollback
    • Practical tips from 16+ years of Moodle™ work

    We email you the checklist link and nothing else. No newsletter, no spam.

    Rating
    5.0

    Based on 20 Google reviews

    Karin Groen

    Karin Groen

    2025

    "From De Schoolschrijver, I really enjoy working with LDesign. The result was a beautiful piece of custom work in our Moodle environment. Vincent is incredibly meticulous, thinks along very actively, and communication is very pleasant. Highly recommended!"

    Arnout Vree

    Arnout Vree

    2023

    "Working with Ldesign is always a pleasure. Clear agreements, short lines of communication, and the quality of the Moodle plugins is good."

    Gemma Lesterhuis

    Gemma Lesterhuis

    2023

    "LT&C has had a pleasant collaboration with Ldesign Media for years for developing and maintaining Moodle LMS plugins. We are very happy with their expertise and professional knowledge."

    Joris Even

    Joris Even

    2023

    "From JE Ontwikkeling, we have been working with Ldesign for several years now. The service is excellent and the collaboration very pleasant. The quality of the programming work is high, and through co-creation we have achieved wonderful products. Thanks!"

    Frans Levels

    Frans Levels

    2020

    "Perfect work for our website."

    Lilian Suijkerbuijk

    Lilian Suijkerbuijk

    2020

    "Very satisfied with our website and the quick actions for questions or changes to be made!"

    Paul Storms

    Paul Storms

    2019

    "Good modification to a Moodle plugin. Clear agreements."

    Merlijn Vanhecke

    Merlijn Vanhecke

    2019

    "Nice service, good knowhow."

    L. Ottink

    L. Ottink

    2019

    "Ldesign manages the maintenance and optimization of our Moodle environment. I am very satisfied with the collaboration with Luuk. He thinks along with us and offers suitable solutions. Through his quick response time and flexibility, he ensures that we are fully taken care of in this area."

    L. Arendsen

    L. Arendsen

    2019

    "Ldesign is a very pleasant company to work with: proactive, fast, excellent results, and clear agreements. Many years of good work delivered for our website."

    Sanne van Hoof

    Sanne van Hoof

    2019

    "Very satisfied: always quick response, quick results, and great that they think along about improvements!"

    Dexo Media

    Dexo Media

    2019

    "Great service, very customer-friendly. Very pleasant to work with."

    Guus Mul

    Guus Mul

    2018

    "Luuk is our technical support and backbone. Very knowledgeable and quick in solving all Moodle-related problems. Highly recommended!!"

    Bas H.

    Bas H.

    2018

    "A very professional, capable, and driven developer. Can think along and develop at all levels."

    Academie Tandartsenpraktijk

    Academie Tandartsenpraktijk

    2018

    "Fast, accurate, proactive, up to date with the latest trends, highly recommended. In other words, a true professional."

    Dental Lect

    Dental Lect

    2018

    "Ldesign is a skilled and extremely professional company. Response times are short and they have never delivered a product we were disappointed with!"

    Josien Drijfhout

    Josien Drijfhout

    2017

    "Friendly and honest advice. Additionally, keeps to agreements and delivers quality in consultation with the client."

    Richard Den Haag

    Richard Den Haag

    2017

    "Luuk is a craftsman like I have never met before. A wonderfully proactive person. I highly recommend Luuk for anything code-related."

    Unhooked Kite

    Unhooked Kite

    2017

    "Good collaboration, and provides good support!"

    Familie Vrolijk

    Familie Vrolijk

    2017

    "Ldesign is a loyal partner for our business website. Accurate, proactive, and helpful. Top company!"

    How can we help?

    Whether you need a custom plugin, a complex integration, or strategic guidance,we're ready to talk. No sales pitch. Just a direct conversation with the team who'll do the work.

    Prefer to talk right away?

    Book a free 15-minute consult

    Opening Hours

    Mon - Fri: 9:00 - 17:00

    Response Time

    Typically within 24 hours

    Direct Access to Developers

    No account managers or sales reps,you'll speak directly with the team who builds your solution.

    Send us a brief

    Your details are treated confidentially and used only to respond to your enquiry. No spam, no newsletters.

    5.0 Google rating · 16+ years of experience · 300+ Moodle™ plugins

    We reply within 24 hours on business days.

    FAQ

    Frequently Asked Questions About Custom Moodle™ Plugin Development

    Common questions about when to build a custom Moodle™ plugin, how we maintain them across upgrades, and how we take over existing plugin portfolios.