Drupal Development With Local AI: Explaining Custom Services

A practical look at how Drupal's service container works and where a locally run AI model genuinely helps—and where it misleads.

Custom services in Drupal are reusable PHP classes registered with Drupal's service container, and a local AI model—one running on your own machine rather than through a cloud API—can speed up how you write, debug, and understand them. This matters because Drupal's service architecture is one of the steepest parts of the learning curve for developers coming from WordPress or plain PHP, and a local AI assistant can walk through that architecture without sending proprietary site code to an external server. This article explains what Drupal custom services actually do, how dependency injection fits in, and where a local AI setup realistically helps versus where it falls short. It also covers the practical side: which local tools exist, what hardware they need, and what to watch for when you let an AI model generate Drupal-specific code.

Table of Contents

What a custom service actually is in Drupal

A Drupal service is a PHP object that performs one job—sending an email, formatting a date, checking a permission—and is made available anywhere in the site through a central registry called the service container. Instead of a module instantiating a class directly with `new`, it asks the container for the service by name, and the container hands back a fully configured object. Services are declared in a `mymodule.services.yml` file, which lists each service's name, its class, and the arguments—usually other services—it needs to run. Drupal reads this file when the container is built and wires everything together automatically.

This is the same dependency injection pattern used in the Symfony framework, which Drupal has relied on since Drupal 8. The payoff is testability and reuse. A service that depends on the database connection or the current user, rather than reaching for global functions, can be swapped out or mocked in a test. It also means a developer writing a new feature doesn't need to know how a dependency is built internally—only that asking the container for `entity_type.manager` or `current_user` will return something that works.

Where local AI fits into this workflow

A local AI model is a large language model that runs entirely on a developer's own computer or private server, using tools such as Ollama, LM Studio, or a self-hosted inference server, instead of calling out to a provider like OpenAI or Anthropic over the internet. For Drupal work, the main appeal is that site code, database schemas, and configuration never leave the machine. In practice, a local model is used the same way a cloud assistant would be: pasted into a chat interface or wired into an editor plugin, then asked to explain a block of code, draft a `services.yml` entry, or suggest how to inject a dependency into a constructor.

Because Drupal's conventions are well documented and widely represented in public code (core modules, contributed modules on Drupal.org), general-purpose models tend to have reasonable familiarity with the pattern even without Drupal-specific fine-tuning. The tradeoff is capability. Local models that fit on a typical development laptop are smaller than the frontier cloud models, and smaller models are more prone to inventing plausible-looking but incorrect service names, YAML keys, or method signatures. Treat local AI output on Drupal internals as a draft to verify against the actual API documentation, not a finished answer.

Writing a services.yml file with AI assistance

When asking a local model to draft a service definition, give it the specific task rather than a vague request. A useful prompt names the service's purpose, lists the dependencies it needs by their actual Drupal service IDs, and asks for both the YAML and the matching PHP class constructor. A typical `services.yml` entry looks like this: The matching class must accept those same arguments, in the same order, in its `__construct()` method and typically implements an interface so other code can type-hint against it rather than the concrete class.

An AI assistant can draft this skeleton quickly, but the service IDs it references—things like `@current_user` or `@entity_type.manager`—need to match real, existing Drupal services. A model that hasn't been checked against current Drupal core will sometimes guess at a service name that doesn't exist or was renamed in a past major version. The safest use of AI here is scaffolding: generating the repetitive boilerplate (the YAML structure, the constructor, a basic interface) so a developer can focus on reviewing the dependency list and the actual business logic inside the methods.

  • `my_module.greeting_service` — the service name other code will use to request it
  • `class: Drupal\my_module\GreetingService` — the fully qualified class the container instantiates
  • `arguments: ['@current_user', '@entity_type.manager']` — other services passed into the constructor, each prefixed with `@`

Checking AI-generated service code before it ships

Before committing AI-drafted service code, a few checks catch the most common mistakes. First, confirm every injected service ID actually exists by running `drush service:list` or searching the relevant `.services.yml` files in Drupal core and installed contributed modules. Second, watch for services that reach too far.

An AI model, especially a smaller local one, will sometimes suggest injecting the entire `entity_type.manager` or `database` service when a narrower, more specific service would do the job with less coupling. This is a general software design concern, not unique to AI assistance, but it shows up more often in generated code because the model optimizes for something that compiles, not something that's architecturally minimal. Finally, test the service in isolation if possible. Drupal's testing framework supports unit tests that swap in mock dependencies, and running one is a fast way to confirm the service actually behaves as intended rather than just looking correct in the file.

  • Verify the class implements `ContainerInjectionInterface` or is registered correctly if it needs to be created via `::create()` in a controller or form.
  • Check that constructor argument order in the PHP class matches the argument order in the YAML file exactly—a mismatch fails silently in confusing ways.
  • Run `drush cache:rebuild` after any `services.yml` change, since Drupal caches the compiled container and won't pick up new or edited services otherwise.
  • Confirm the namespace in the class file matches the module's PSR-4 autoloading path (`src/` followed by the namespace structure).

Frequently Asked Questions

Does a local AI model need internet access to help with Drupal code?

No. Once the model and its runtime (such as Ollama) are installed, inference happens entirely on the local machine, though the initial model download requires a connection.

Can a local AI model replace reading Drupal's API documentation?

No. Local models, especially smaller ones that run on consumer hardware, can misstate service names or method signatures, so generated code should be checked against the actual Drupal API reference.

Is dependency injection required for every Drupal module?

No. Simple modules can use procedural hooks without ever defining a service, but any code that needs to be testable, reusable, or swapped between environments benefits from being structured as a service.


You Might Also Like