Een verzoek van een product owner: zijn Drupal website heeft een content-type Evenement. De redactie wil op de homepage een blok met de eerstvolgende drie evenementen tonen.
De initiële wens en AI prompt:
“Maak een Drupal block dat de eerstvolgende drie evenementen toont, gesorteerd op startdatum. Geef elk evenement een titel, datum en link. De evenementen moeten tevens gepubliceerd zijn. Houdt ook rekening met herhaalde evenementen. Graag 'programatically', niet in Views, omdat ik meer complexiteit en externe integraties verwacht later in dit ontwikkeltraject ”
In plaats van eerst alle code zelf uit te werken, kan een Drupal developer AI als programmeerpartner gebruiken. Dat is vibe coding: je beschrijft vooral wat je wilt bereiken, laat AI een eerste implementatie genereren, en ontwikkelt verder op basis van testen en feedback.
Stap 1 – De Drupal opdracht aan AI
Een eerste prompt kan bijvoorbeeld zijn:
Ik werk met Drupal 11.
Ik heb een contenttype 'Evenement' met de velden:
- field_start_date
- field_location
Maak een custom module 'event_teaser' met een block plugin.
Het block moet:
- de 3 eerstvolgende gepubliceerde evenementen ophalen;
- sorteren op field_start_date oplopend;
- titel, startdatum en locatie tonen;
- een link naar de node tonen;
- geen evenementen tonen als er geen toekomstige evenementen zijn.
Gebruik Drupal coding standards en dependency injection waar dat relevant is.
Geef de benodigde bestanden en code.
AI kan vervolgens bijvoorbeeld een modulestructuur voorstellen:
modules/custom/event_teaser/
├── event_teaser.info.yml
├── event_teaser.module
└── src/
└── Plugin/
└── Block/
└── UpcomingEventsBlock.php
Stap 2 – De eerste code
Een vereenvoudigde block plugin kan er bijvoorbeeld zo uitzien, in eerste instantie gegenereerd door AI:
<?php
namespace Drupal\event_teaser\Plugin\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides an Upcoming Events block.
*
* @Block(
* id = "upcoming_events",
* admin_label = @Translation("Upcoming events")
* )
*/
class UpcomingEventsBlock extends BlockBase implements ContainerFactoryPluginInterface {
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected EntityTypeManagerInterface $entityTypeManager;
/**
* Constructs the block.
*/
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
EntityTypeManagerInterface $entity_type_manager,
) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->entityTypeManager = $entity_type_manager;
}
/**
* {@inheritdoc}
*/
public static function create(
ContainerInterface $container,
array $configuration,
$plugin_id,
$plugin_definition,
) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity_type.manager'),
);
}
/**
* {@inheritdoc}
*/
public function build() {
$storage = $this->entityTypeManager
->getStorage('node');
$query = $storage->getQuery()
->accessCheck(TRUE)
->condition('type', 'evenement')
->condition('status', 1)
->condition('field_start_date', date('Y-m-d\TH:i:s'), '>=')
->sort('field_start_date', 'ASC')
->range(0, 3);
$nids = $query->execute();
$nodes = $storage->loadMultiple($nids);
return [
'#theme' => 'item_list',
'#items' => array_map(
fn($node) => [
'#markup' => $node->toLink()->toString(),
],
$nodes
),
];
}
}
De vibe coding zit echter niet alleen in het genereren van deze code. Het interessante deel begint daarna.
Stap 3 – Testen
De developer installeert de module:
drush en event_teaser
drush cr
Daarna kan je het block binnen het backend in een Drupal region plaatsen, zodat deze zichtbaar wordt in het frontend.
Het frontend resultaat is dan bijvoorbeeld:
Aankomende evenementen
Drupal Meetup Amsterdam
15 oktober 2026
Web Accessibility Workshop
22 oktober 2026
Open Source Conference
4 november 2026
Maar tijdens het testen en code review vallen dingen meteen op:
- de datum wordt bijvoorbeeld verkeerd weergegeven,
- de locatie ontbreekt,
- waarom wordt hier
getQuery() gebruikt, en niet eenentityQuery`? - en what the * bleep * doen
fn($node)enarray_map()hier?
Maar ook:
- is de dependency injection wel correct gedaan,
- hoe zit het met code security?
- en veel meer (zie ook hieronder).
Je kunt de code ook nog door Drupal Coder module halen, ter review.
Stap 4 – Feedback geven aan AI
In plaats van zelf de hele implementatie opnieuw te schrijven, geeft de Drupal developer concrete feedback:
De eerste versie werkt, maar ik wil drie wijzigingen:
1. Gebruik Drupal's date formatter in plaats van date().
2. Toon field_location onder de titel.
3. Gebruik een aparte Twig-template file in plaats van #markup.
4. Wat doet "fn($node)" hier?
Pas de module aan en geef alleen de bestanden die moeten wijzigen.
AI kan vervolgens een Twig-template voorstellen:
event_teaser/
├── templates/
│ └── event-teaser-item.html.twig
└── src/
└── Plugin/
└── Block/
└── UpcomingEventsBlock.php
Met bijvoorbeeld:
<article class="event-teaser">
<h3>
<a href="{{ url }}">{{ title }}</a>
</h3>
<time datetime="{{ datetime }}">
{{ date }}
</time>
{% if location %}
<div class="event-teaser__location">
{{ location }}
</div>
{% endif %}
</article>
Stap 5 – Een probleem ontdekken
Tijdens code review merkt de Drupal developer op dat de query:
->condition(
'field_start_date',
date('Y-m-d\TH:i:s'),
'>='
)
mogelijk niet goed aansluit bij het daadwerkelijke opslagformaat van het datumveld.
Dit is een belangrijk onderdeel van vibe coding:
AI genereert de code, maar de Drupal developer blijft verantwoordelijk voor de juistheid ervan.
De Drupal developer moet zelf de code kunnen testen en handmatig kunnen corrigeren, want AI kan ook nog flink hallucineren.
De developer kan AI daarom vragen:
Controleer deze query specifiek voor Drupal 11 en een
datetime field.
Leg uit welk formaat Drupal intern gebruikt voor
field_start_date en pas de query aan.
Geef ook aan welke aannames je maakt.
AI kan dan helpen de implementatie te corrigeren, maar de developer controleert vervolgens de oplossing tegen de daadwerkelijke Drupal configuratie en testdata.
Stap 6 – Een extra verbetering
Na de functionele versie komt een nieuwe wens:
“Kunnen we het aantal evenementen configureerbaar maken?”
De developer vraagt:
Maak het block configureerbaar.
De beheerder moet in de block configuration kunnen
kiezen hoeveel evenementen worden getoond, standaard 3.
Gebruik Drupal Form API en sla de configuratie op
als block configuration.
AI genereert vervolgens de benodigde blockForm(), blockSubmit() en aanpassingen aan build().
De developer hoeft daardoor niet elk stukje Drupal API-code uit het hoofd te kennen. Hij stuurt vooral het gewenste gedrag en beoordeelt de gegenereerde implementatie.
Ook hier geldt weer: de Drupal developer moet kunnen analyseren of de gegenereerde code in blockForm(), blockSubmit() en build() kloppen.
Wat maakt dit 'vibe coding'?
In een traditionele aanpak zou de developer bijvoorbeeld:
- de Drupal Block API documentatie opzoeken;
- de pluginstructuur maken;
- de dependency injection schrijven;
- de Entity Query opbouwen;
- een Twig-template maken;
- de configuration form schrijven;
- testen en debuggen.
Drupal console en Drush hielpen daar voorheen bij, maar vibe coding verschuift een deel van dat werk naar een gesprek met AI:
Developer
│
│ "Ik wil dit gedrag"
▼
AI
│
│ code + voorstel
▼
Developer
│
│ testen
▼
Probleem / feedback
│
└──────────────► AI
│
▼
verbeterde code
De developer werkt dus meer als regisseur en reviewer en minder als iemand die iedere regel vanaf nul typt.
Waar moet je in Drupal extra op letten?
Vibe coding is vooral krachtig als de developer de Drupal best practices goed blijft bewaken.
Let bijvoorbeeld op:
- Security — controleer input, permissions en output.
- Drupal coding standards — laat gegenereerde code niet automatisch als correct beschouwen.
- Upgradebaarheid — controleer of gebruikte API's daadwerkelijk bij de Drupal-versie passen.
- Dependency injection — vermijd onnodig gebruik van globale services.
- Configuration management — zorg dat instellingen exporteerbaar zijn.
- Cacheability — blocks en entity queries moeten correct gecachet worden.
- Access checking — bij alle 'Routes' en 'Entity Queries'.
- Render arrays — vermijd onnodig HTML via
#markup. - Twig escaping — laat Drupal het escapen van output afhandelen.
- .. en vele andere Drupal best practices
De kern
Een goede Drupal-vibe-coding workflow is daarom niet:
“AI, bouw mijn Drupal website.”
Maar eerder:
“Ik beschrijf het gewenste gedrag → AI maakt een eerste implementatie → ik test → AI help mij verbeteren → ik review en accepteer de uiteindelijke code.”
Het voordeel zit vooral in de kortere feedbacklus. De Drupal developer kan sneller van idee naar werkende Drupal-code gaan, terwijl kennis van Drupal-architectuur, security en onderhoudbaarheid belangrijk blijft.
Full disclosure: dit blog is tevens vibe coded tot stand gekomen :)
