Picnic logo

Tackling Configuration: creating Lego-Like Flexibility for non developers

Written by Joachim van SchothorstFeb 6, 2025 10:0115 min read
1*pe aQxxblL9htJ4HExnL A

In our previous blogs, we explored how Picnic’s Page Platform transformed the way we build new features — enabling faster iteration, tighter collaboration and less feature-specific complexity. Advances we made there have been a major boost for productivity and allow us to focus on the things that actually deliver customer value. However, while focussing on our technical architecture, we haven’t yet discussed one major opportunity: bringing our commercial and customer teams into the mix.

At Picnic, analysts and store managers are a core part of our team. Data is in our DNA, and with our goal of making the experience for customers a bit better everyday, we’re constantly discovering new insights. In this capacity, our analysts and store managers play a crucial role in fine-tuning every element of the store and customer journey. They’re responsible for configuring the assortment, recipes, and items in promotion. Whenever a story around something needs to be told, they make sure it happens.

With the advancements we made in developer experience with the Page Platform, we saw the opportunity to provide similar flexibility to our store managers and analysts in the configuration of our store. Just as our engineers needed better tools to ship changes faster, these teams need tools that empower them to make real-time decisions with confidence, flexibility, and precision.

This took us on the journey: How can we build tools that make it easier to experiment with configurations, access insights instantly, and adapt business rules? How can we bring the same principles of modularity, flexibility, and real-time adaptability from our app platform into configuration?

In this blog, we’ll dive into how we configure the pages within Picnic’s store. After creating extensive flexibility using the page platform, we subsequently built tools to leverage this flexibility. Join us as we explore how empowering our commercial teams can drive the customer experience to the next level.

What should be configurable?

Within our tech team, we follow a key principle: make anything that could vary due to business logic configurable. Core technical capabilities belong in our systems, but business rules should be dynamic. This approach helps us balance two needs: building stable, scalable systems while enabling rapid iteration on customer experience based on real-world feedback.

This approach is extremely valuable, although we do a lot of research & design work on new features. Often the real proof is in the pudding: shipping a product and seeing how customers respond. Subsequently, keep what is good and iterate on what could be better.

We’ve already successfully implemented this principle in several areas. Our Rule Engine for Real-Time Action Automation first brought this flexibility to customer-facing features, enabling event-driven customer interactions. We later extended this principle to internal tools with our Edge Systems Environment.

Now, we’re bringing this same principle to our app. As detailed in our Server-Driven UI in Picnic’s Page Platform blog post, our server-driven UI framework is extremely flexible — instead of high-level components like “recipe-section,” it works with raw rendering instructions for basic elements like text, rectangles, and images. This low-level approach means anything can be built from simple building blocks. However, with such flexibility comes responsibility — we need robust configuration tools to ensure we use this power wisely. Our new configuration tools provide guardrails and validation while preserving the power of the underlying system.

This configuration happens at three levels:

  • Layout of any page: The structural composition of pages using basic elements. How does it look and how is it built up (i.e. carousel of cards with an image and text)
  • Business logic: Rules determining what to show within that layout (i.e. highlight specific image and text in the carousel based on time of day)
  • Content: The actual assets and information that populate these layouts and might be selected by business logic

Defining layout

With the Page Platform, we made it significantly easier to modify page layouts. This was one of our first major steps toward creating more flexibility for our business teams. Building on this foundation, we developed a layer on top of our server-side components that enables page creation through a drag-and-drop builder: the Store Page Configurator.

The Store Page Configurator offers our commercial team a Lego-like toolbox to build pages independently, with several key features:

  • Management of the app’s page structure, enabling control over navigation flows and page relationships
  • A visual editor for component manipulation through drag-and-drop — moving, restructuring, and organizing components to create layouts
  • Built-in preview functionality for testing changes before deployment

To build this tool, we minimized inherent business logic and kept our systems to core concepts. To do so, we generalized what we already had:

  1. The components that we built already had a schema defining what input they needed. Expanding this type-based schema with some additional metadata allowed us to autogenerate the UI for whatever configuration parameters a component needs.
  2. To configure pages, we already had our own DSL. By using that same DSL to express layouts configured in the Store Page Configurator, we could reuse the existing publishing infrastructure.
  3. For testing and validation of configuration, we do semantic validation based on additional metadata defined by the developer creating a component, next to type checking based on the schema of pages. The real power, however, comes from rendering the page over our normal pipeline, allowing internal users to handle full testing & validation by previewing the page in their app before it’s live for others.

After two years of use, we’ve seen and learned a lot. The auto-generated UI approach has been particularly successful — the effort required to maintain configurability has been incredibly low! A developer only has to do some small JSON schema changes to make a component available in the Store Page Configurator.

The page configurator (right) with a representation in the app (left)

Defining logic

When we selected the core components of our architecture, we chose Calcite since within, SQL is the lingua franca. Our analysts use it to gather data and do their analysis, while most developers are familiar with it from at least their education.The various roles in between also come across a query or 10 a day.

We figured out that any business rule can be reasonably expressed in SQL (see also the second blog in this series or the Calcite deepdive we recently wrote here). Having figured this out, we realized that this was the perfect way to define logic within pages.

Configuring these queries can directly be done for elements in our layout, however, to allow for rapid iteration and testing, we realized that we needed to build a dedicated tool. Our second tool was born: Calcite query playground, with features like:

  • Table exploration for our Calcite tables
  • Documentation on key use cases
  • Performance analysis tooling to tweak query performance
The Calcite Query Playground

Managing content

Initially, we had just built the two tools described above. Besides some info in our Master data and ranking system, this meant that a lot of configuration of content (i.e. what image to show to highlight a certain deal) was generally hardcoded in the layouts. This led to various challenges in maintainability. For example, a good discount typically needs to be highlighted in various places in the app, however, once sold out, these highlights then also need to be removed again to prevent our customers from getting disappointed.

The first hurdle we tried to overcome was to create the option to personalize more based on data. To make initial steps here, we built a generalized datastore that is well integrated in our broader ecosystem. In this blog we won’t go into details, but having built this datastore there is actually generally two types of data when personalizing, not just who sees what content, but also different variations of the content to show.

With the data editor, we added a generic place to manage any content to our toolbelt. This content can subsequently be used in any page or be used in any logic defined using SQL queries.

Having seen how valuable schema-based config was in the page configurator. We again took this approach. Our datastore already required that for any table, all columns had to be defined by a name and primitive type (i.e. is something a date, a number or a string of text). To make a useful CMS-like tool, we allowed people to also specify a custom type for a column (i.e. does a column contain a recipe ID). This allows us to validate very specific configuration in the Picnic context, without having to build use case specific logic for handling a new table or a new combination of different fields.

However, we wanted to go a step further: for any custom column type (let’s assume a column containing recipe IDs as example), we effectively have three key actions:

  • Validate: ensure that some value is a valid recipe ID
  • Search: allow searching for some specific recipe in our database
  • Preview: show any related metadata based on the recipe ID entered in a row (for example the recipe image)

Knowing that our core entities are exposed to Calcite, any of the above operations can be defined as a SQL query. This creates a beautifully generic tool, where any table can be defined by a number of columns. And these columns can represent complex domain-level types, where behaviour in the data editor can just be represented by three new queries.

Once we realized that we had built a solid core, we started extending this tool with a lot of usability and quality of life improvements such as allowing bulk editing and importing, and easy filtering based on column type.

Example table in our data editor

Bringing this configuration together

To illustrate how these tools work in symbiosis, it’s helpful to walk through an example. Let’s assume we’d like to create a page where we highlight a seasonal vegetable that is in discount which customers haven’t bought before.

We might want to create a bit of a story for different products & show a nice highlight image. To manage the configuration we might create a table seasonal_veg_highlights of shape:

A content creator might start creating content for different seasonal vegetables highlighting different features. This would all happen via the data editor.

We however still need to create the logic of who should do what: through iteration in the query playground, we might end up with a query like the following:

SELECT svh.*
FROM seasonal_veg_highlights svh
JOIN product p on p.id = svh.product_id and p.in_season = TRUE
JOIN active_promo ap on ap.product_id = p.id
LEFT join order_history oh ON svh.product_id = oh.product_id
WHERE oh.product_id IS NULL
Limit 1

I.e. a query where we select a highlight based on whether the product is in season & promo, yet doesn’t yet appear in the customers order history.

Then lastly we can go to the Store Page configurator where we create a new page, drop in the elements we want to use to present the highlight. In this element, we include the query we created to ensure the right data is included in the page. We can then quickly preview the page to verify everything looks correct, and with a single click, publish it to all customers immediately: a configurator’s dream come true.

Looking ahead

Exposing the flexibility of the Picnic page platform towards business stakeholders has been a major change for the way we run our business. We have massively increased flexibility through configuration in our store. We are now in a state where our business team can directly change business logic, layout & content without tech involvement.

While increasing the flexibility, we have been able to mature our ecosystem and tooling, creating a full environment where we’re not just doing data validation, but actually make it a nice experience to manage the store

Does that mean that we’re done building? Absolutely not: there is still a lot to improve on, including building tooling for better personalization. We are determined on giving every customer a personal experience tailored to their needs. This requires us to better curate what we show our customers, but also tailor & create content relevant to specific subgroups. This topic is incredibly exciting, but deserves a separate deep-dive.

Excited to learn more? Stay tuned for upcoming blog posts in this series. Next time we’ll dive into how we majorly improved developer experience when working with Page Platform and brought TypeScript into the mix!

This article is part of a series of blogposts on the architecture behind our grocery shopping app. Hungry for more? Chew off another bite with any of our other articles:


Tackling Configuration: creating Lego-Like Flexibility for non developers was originally published in Picnic Engineering on Medium, where people are continuing the conversation by highlighting and responding to this story.

picnic blog job banner image

Want to join Joachim van Schothorst in finding solutions to interesting problems?