▶ Practice Mode
Difficulty:
Quick Tip

Show the performance advantage: "Symfony compiles the container to raw PHP at warmup. In production, every service resolution is a direct method call with no reflection or runtime resolution — that is why it is fast."

What good answers include

The Symfony service container is a compiled dependency injection container. At build time, it resolves all dependencies, generates optimised PHP code, and caches it. Autowiring resolves dependencies by type-hint — if only one service implements an interface, Symfony injects it automatically. Explicit definitions are needed when multiple implementations exist for the same interface, when you need tagged services, or when constructor arguments are scalar values. Strong candidates mention the compiler pass system, auto-configuration via attributes, and that the compiled container has zero runtime resolution cost.

What interviewers are looking for

Core Symfony knowledge. Candidates who cannot explain the compiled container do not understand why Symfony behaves differently in dev versus prod. Those who manually wire everything when autowiring would suffice are creating unnecessary work.

Permalink →
Quick Tip

Lead with the profiler: "I open the Symfony debug toolbar, check the Doctrine panel. If I see 50 queries for a list of 50 items, that is an N+1 problem. I add a fetch join in the repository query and it drops to 1."

What good answers include

Systematic approach: enable the Symfony profiler and check the Doctrine panel for query count and timing. Common issues: N+1 queries (fix with DQL joins or fetch joins), missing database indexes, hydrating full entities when a DTO projection suffices, excessive flush operations, and lazy-loading in loops. Solutions: use QueryBuilder with addSelect for eager loading, use NEW DQL syntax for DTO hydration, add indexes via Doctrine attributes, batch operations with iterate(), and consider read replicas. Strong candidates mention the debug toolbar query count as the first diagnostic step.

What interviewers are looking for

Senior Symfony question. Developers who cannot diagnose N+1 queries will build slow applications without knowing why. Those who jump to caching before fixing the query layer are masking the problem.

Permalink →
Quick Tip

Show the layers: "Firewalls handle who you are. Access control handles which paths require which roles. Voters handle can-this-user-do-this-to-this-object — that is where business logic lives."

What good answers include

Firewalls define authentication boundaries — which routes require login and how users authenticate (form login, API token, etc.). Access control rules in security.yaml provide simple path-based role checks. Voters are the fine-grained authorisation layer — custom classes that vote GRANT, DENY, or ABSTAIN on specific attributes and subjects. The access decision manager aggregates votes (default: affirmative strategy). Strong candidates explain when to use each: access_control for broad route protection, voters for object-level permissions like "can this user edit this post."

What interviewers are looking for

Tests understanding of Symfony security architecture. Candidates who put all authorisation logic in controllers are missing the voter system. Those who understand the layered approach write more maintainable and testable security code.

Permalink →
Quick Tip

Show the flow: "The form maps request data to the entity, transformers handle type conversion, then validation constraints fire. If valid, the controller gets a hydrated entity ready to persist."

What good answers include

Form types define the structure and mapping between form fields and entity properties. Data transformers convert between the form representation and the model representation — for example, transforming an entity ID to an entity object. Validation constraints (annotations/attributes on entities or inline in form types) run after the form is submitted and before the data reaches the controller. Strong candidates discuss: compound forms (embedding form types), custom form types, the form events system, and the difference between model and view transformers.

What interviewers are looking for

Tests practical Symfony experience. Candidates who bypass the form system for manual request handling lose validation, CSRF protection, and type safety. Those who understand data transformers can handle complex form scenarios.

Permalink →
Quick Tip

Give a concrete example: "I use a kernel.response subscriber to add Cache-Control and CSP headers to every response. This keeps controllers focused on business logic and ensures no endpoint misses the security headers."

What good answers include

Symfony dispatches events at key points in the request lifecycle: kernel.request, kernel.controller, kernel.response, kernel.exception, kernel.terminate. Event subscribers listen to these events and execute cross-cutting logic. Use cases: adding security headers on every response, logging request metadata, modifying responses globally, handling exceptions with custom error pages, and running post-response cleanup. Strong candidates discuss: event priority, the difference between listeners and subscribers, and when middleware-style logic belongs in an event subscriber versus a controller.

What interviewers are looking for

Tests architectural understanding. Candidates who duplicate cross-cutting concerns across controllers do not understand the event system. Those who use kernel events for request-scoped logic and kernel.terminate for post-response work demonstrate framework maturity.

Permalink →
Quick Tip

Show the layers: "I unit test services with mocked repositories. Functional tests with WebTestCase cover the full request cycle — routing, security, persistence. I use SQLite for speed in CI and run a MySQL suite before deploy."

What good answers include

Unit tests: test services and entities in isolation with PHPUnit, mock dependencies. Functional tests with WebTestCase: boot the kernel, create a client, make HTTP requests, and assert on responses — tests the full stack including routing, security, and database. Use KernelTestCase when you need the container but not HTTP. Strong candidates discuss: test databases (SQLite for speed or matching production DB for accuracy), fixtures, transaction rollback between tests, and the balance between fast unit tests and confidence-building functional tests.

What interviewers are looking for

Tests professional development practices. Candidates who only write unit tests miss integration bugs. Those who only write functional tests have slow suites. Look for a balanced approach with clear reasoning about what each layer covers.

Permalink →
Quick Tip

Emphasise safety: "I never use schema:update in production. Every change goes through a migration file that I review before committing. Deployment runs migrate automatically. For breaking changes, I split into add-column, backfill, and add-constraint steps."

What good answers include

Workflow: modify entity attributes, run doctrine:migrations:diff to generate a migration, review the generated SQL, run doctrine:migrations:migrate to apply. In deployment: migrations run automatically as part of the deploy pipeline before the new code serves traffic. Strong candidates discuss: idempotent migrations for safety, handling data migrations separately from schema changes, the risks of doctrine:schema:update --force in production, and strategies for zero-downtime migrations (adding columns as nullable first, backfilling, then adding constraints).

What interviewers are looking for

Baseline Symfony/Doctrine workflow. Candidates who use schema:update in production or who do not review generated migrations will cause incidents. Look for awareness of zero-downtime migration strategies.

Permalink →
Quick Tip

Show practical use: "I dispatch a GenerateReport message to a Redis transport. A worker process picks it up, generates the PDF, and stores it. The user gets a notification when ready. Failed messages go to the failure transport for inspection."

What good answers include

Messenger dispatches message objects to handlers via a bus. Transports (Doctrine, Redis, AMQP, Amazon SQS) move messages between the bus and workers for async processing. Use cases: sending emails, processing uploads, generating reports, and any work that does not need to complete within the HTTP request. Strong candidates discuss: routing messages to specific transports, retry strategies and failure transports, serialisation, stamps for metadata, and the difference between command bus and event bus patterns.

What interviewers are looking for

Tests knowledge of async processing in Symfony. Candidates who do everything synchronously in controllers will build slow applications. Those who understand transports, retry strategies, and failure handling can build resilient async workflows.

Permalink →
Quick Tip

Layer the caches: "HTTP caching with Varnish for public pages, Redis application cache for expensive service calls, and Doctrine result cache for reference data that changes daily. Each layer has different invalidation strategies."

What good answers include

HTTP caching: Cache-Control headers, ETags, and reverse proxies (Varnish, Symfony HttpCache). Best for public, cacheable pages. Application caching: Symfony Cache component with adapters (Redis, APCu, filesystem) for expensive computations or API results. Doctrine result cache: caches query results in a PSR-6 pool, useful for read-heavy data that changes infrequently. Strong candidates discuss cache invalidation strategies, cache tagging for selective purging, and the trade-off between cache hit rate and data freshness.

What interviewers are looking for

Senior question testing performance architecture. Candidates who only think of one caching layer miss optimisation opportunities. Those who discuss invalidation strategies (the hard part) alongside caching (the easy part) demonstrate real experience.

Permalink →
Quick Tip

Show the modern approach: "Application code goes in src/ with autowiring and attributes. I only create a bundle when the code needs to be distributed as a Composer package across multiple projects."

What good answers include

Modern Symfony (4+) moved away from bundles for application code. Application logic lives in src/ with autowiring — no bundle needed. Bundles are for reusable, distributable packages shared across projects (like a payment SDK or an admin panel). The shift happened because application bundles added boilerplate (Extension class, Configuration class, bundle registration) with no benefit for code that only runs in one project. Strong candidates explain: when to extract a bundle (shared across multiple projects), Flex recipes for third-party bundles, and the role of compiler passes in bundles versus service configuration in applications.

What interviewers are looking for

Tests whether candidates understand modern Symfony conventions versus legacy patterns. Those who create application bundles for every feature are following outdated practices. Look for understanding of when the bundle abstraction adds genuine value.

Permalink →
Quick Tip

Show the decision: "I use ManyToOne unidirectional when I only need to traverse one direction. Bidirectional adds convenience but also responsibility — I must maintain both sides in code and be aware of cascade implications."

What good answers include

Doctrine supports OneToOne, OneToMany, ManyToOne, and ManyToMany. Unidirectional associations only define the relationship on one side — simpler but you cannot traverse from the inverse side. Bidirectional requires an owning side (where the foreign key lives) and an inverse side (mappedBy). Performance: bidirectional ManyToMany creates a join table and can lead to large hydrations; consider replacing with a linking entity for extra columns. Lazy loading is the default — collections are loaded on first access. Strong candidates discuss: cascade operations (persist, remove), fetch modes (LAZY, EAGER, EXTRA_LAZY), orphanRemoval for cleanup, and the cost of hydrating large collections versus using DQL pagination.

What interviewers are looking for

Core Doctrine knowledge. Candidates who cannot explain the owning side concept will create inconsistent data. Those who understand EXTRA_LAZY for large collections and orphanRemoval for cleanup demonstrate production experience.

Permalink →
Quick Tip

Show the precedence: "Real env vars override everything. For dev, .env provides defaults, .env.local overrides them locally. In production, I set real environment variables through the hosting platform — never rely on .env files."

What good answers include

Symfony loads .env files in a specific order: .env (defaults), .env.local (local overrides, gitignored), .env.{env} (environment-specific), .env.{env}.local (local environment overrides). Real environment variables always take precedence over .env files. Config files under config/packages/ can be environment-specific: config/packages/prod/monolog.yaml only loads in prod. The APP_ENV variable determines which environment is active. Strong candidates explain: that .env files are for development convenience and production should use real environment variables, the %env()% syntax in service configuration, and env var processors (bool, int, json, file, etc.).

What interviewers are looking for

Baseline Symfony knowledge. Candidates who commit secrets to .env files or do not understand the loading order will create configuration bugs across environments. Ask how they handle secrets in production.

Permalink →
Quick Tip

Show clean architecture: "Base template defines blocks for content, sidebar, and scripts. Pages extend it and override only what they need. Complex display logic goes into a Twig extension or entity method, not inline in the template."

What good answers include

Template inheritance: a base layout (base.html.twig) defines blocks that child templates override. Twig components (Symfony UX) provide reusable UI elements with their own logic class. Keep templates thin: complex logic belongs in Twig extensions (custom filters/functions), entity methods, or controller-prepared variables — not in {% if %} chains in templates. Strong candidates discuss: the block system for layout composition, the include and embed tags for partials, Twig namespaces for organising templates, and the importance of escaping (autoescaping is on by default, use the raw filter deliberately).

What interviewers are looking for

Tests template architecture thinking. Candidates who put business logic in Twig or who do not use template inheritance effectively will create hard-to-maintain views. Those who leverage blocks, components, and extensions keep templates readable.

Permalink →
Quick Tip

Match the tool to the need: "For a data-driven API with standard CRUD, API Platform saves weeks of work. For an API with complex business workflows that do not map to entities, custom controllers with the Serializer give me full control."

What good answers include

API Platform: automatic CRUD endpoints from Doctrine entities via PHP attributes, built-in pagination, filtering, validation, OpenAPI documentation, and content negotiation. Extremely fast for standard REST APIs. Custom controllers with Symfony Serializer: more control, better for non-CRUD operations, complex business logic, or APIs that do not map to entities. Trade-offs: API Platform adds magic and a learning curve but saves enormous time for data-driven APIs. Custom approach is more verbose but explicit. Strong candidates discuss: when to use API Platform state processors for custom logic, DTO input/output for decoupling API shape from entities, and when the framework overhead is not worth it for simple endpoints.

What interviewers are looking for

Tests API architecture in Symfony. Candidates who always build everything from scratch may not know API Platform. Those who force API Platform on non-CRUD APIs are fighting the framework. Look for pragmatic tool selection.

Permalink →
Quick Tip

Show the async pattern: "I configure Mailer to use Messenger. When the controller sends an email, it is dispatched to the queue and a worker sends it. The user gets an instant response. Failed emails land in the failure transport for retry."

What good answers include

Symfony Mailer: create Email or TemplatedEmail objects, send via MailerInterface. For async delivery: configure Messenger transport and Mailer routes messages through it automatically — emails are queued and sent by workers, keeping HTTP responses fast. The Notifier component extends this to SMS, Slack, Telegram, and other channels via a unified interface. Strong candidates discuss: Mailer DSN configuration for providers (SendGrid, Mailgun, SES), email testing with the profiler and Mailtrap, Twig-based email templates with inlined CSS, and using Notifier channels to avoid scattering notification logic across the codebase.

What interviewers are looking for

Tests understanding of async processing in web applications. Candidates who send emails synchronously in controllers will create slow endpoints. Those who understand Mailer + Messenger integration build responsive applications.

Permalink →
Quick Tip

Show practical implementation: "I define a fixed_window limiter for login attempts — 5 per minute per IP. The controller injects the factory, calls consume(), and throws a TooManyRequestsHttpException with Retry-After header if the limit is hit."

What good answers include

Symfony RateLimiter provides token bucket and sliding window algorithms. Configure limiters in framework.yaml with rate_limiter key, defining policy, limit, and interval. Inject using #[Autowire] with the limiter factory, then call consume() to check and enforce limits. Use cases: login attempts (prevent brute force), API endpoints (throttle per API key), form submissions (prevent spam), and password reset requests (prevent email bombing). Strong candidates discuss: different algorithms (token bucket allows bursts, sliding window is stricter), using client IP versus user ID as the limiter key, returning proper 429 responses with Retry-After headers, and storing limiter state in Redis for multi-server setups.

What interviewers are looking for

Tests security-aware development. Candidates who do not rate limit sensitive endpoints leave applications vulnerable to brute force and abuse. Those who understand different algorithms and choose the right one for each use case build secure systems.

Permalink →
Quick Tip

Show the layers: "Normalizers turn objects into arrays, encoders turn arrays into JSON. I use serialization groups to expose different fields for list versus detail endpoints, and a circular reference handler that returns the entity ID."

What good answers include

The Serializer converts objects to arrays (normalization) then arrays to formats like JSON/XML (encoding), and vice versa. Normalizers handle the object-to-array step: ObjectNormalizer uses getters/setters, PropertyNormalizer accesses properties directly. Encoders handle array-to-format: JsonEncoder, XmlEncoder, CsvEncoder. Circular references: configure a handler (typically returning the ID) via the circular_reference_handler context option. Serialization groups (#[Groups]) control which properties are included in different contexts (list vs detail). Strong candidates discuss: custom normalizers for complex transformations, max depth handling for nested objects, DTO serialization for decoupling API responses from entities, and the name converter for snake_case to camelCase mapping.

What interviewers are looking for

Tests deep Symfony knowledge. Candidates who JSON-encode entities manually or who struggle with circular references do not understand the Serializer. Those who use groups, custom normalizers, and DTOs build clean APIs.

Permalink →
Quick Tip

Trace the flow: "The form authenticator extracts email and password from the request, the user provider loads the entity by email, the password hasher verifies the hash, and the security system creates an authenticated token stored in the session."

What good answers include

User providers load user objects from storage (entity provider from the database, memory provider for testing). Authenticators handle the authentication logic: extracting credentials from the request, validating them, and creating the security token. The password hasher handles secure password storage (bcrypt or argon2id). Flow: firewall triggers the authenticator, which gets credentials, loads the user via the provider, verifies the password via the hasher, and creates an authenticated token. Strong candidates discuss: custom authenticators for API tokens or JWT, remember-me functionality, login throttling, the security token and how it relates to the session, and logout handlers.

What interviewers are looking for

Core Symfony security knowledge. Candidates who cannot explain the authentication flow will struggle to debug login issues or implement custom authentication. Those who understand the full chain from request to token build secure auth systems.

Permalink →
Quick Tip

Show good structure: "My commands are thin — they parse input, call a service, and format output with SymfonyStyle. The business logic lives in the service so it can be reused by controllers and tested independently."

What good answers include

Console commands extend Command or use the #[AsCommand] attribute. Define arguments (required positional values) and options (optional flags) in configure(). Use SymfonyStyle for formatted output, progress bars for long operations, and the QuestionHelper for interactive input. Commands are ideal for: scheduled tasks (cron), data migrations, maintenance operations, and developer tooling. Controllers handle HTTP requests. Strong candidates discuss: lazy command loading for performance, command locking to prevent parallel execution, injecting services via the constructor, and writing testable commands by keeping logic in services.

What interviewers are looking for

Tests practical Symfony development. Candidates who put complex business logic directly in command execute() methods create untestable, unreusable code. Those who keep commands as thin wrappers around services demonstrate good architecture.

Permalink →
Quick Tip

Show the trade-off: "I use Foundry factories for entities needed in many tests — they are concise and handle relationships. For test-specific edge cases, I create entities inline so the test is self-contained and obvious."

What good answers include

Doctrine Fixtures (DoctrineFixturesBundle): PHP classes that create database records, useful for development seeding and test baselines. Factory libraries (Foundry, Alice): generate objects with random but realistic data, support states and relationships. Inline setup: create entities directly in test methods for maximum clarity. Best practice: use factories for common entities needed across many tests, inline creation for test-specific data, and fixtures for development environment seeding. Strong candidates discuss: test database isolation (transaction rollback or separate database), fixture ordering with getDependencies(), and the trade-off between shared fixtures (fast but coupled) and per-test setup (slow but independent).

What interviewers are looking for

Tests testing maturity. Candidates with no strategy for test data will write slow, brittle, or coupled tests. Those who use factories for common data and inline creation for specific cases balance speed with clarity.

Permalink →
Quick Tip

Show diagnostic workflow: "First thing I check is the Doctrine panel — if the query count is high, I have an N+1 problem. Then the Security panel if access control is behaving unexpectedly. The profiler is my first stop before any other debugging tool."

What good answers include

The debug toolbar shows: request info, response status, route, controller, database queries (count and time), memory usage, Twig renders, logged messages, and security context. Click to expand the full profiler with detailed panels. Key panels: Doctrine (every SQL query with parameters), Security (authenticated user and voter decisions), Events (dispatched events and listeners), and Mailer (sent emails). For production: use the profiler sparingly (enable for specific IPs), or use APM tools (Blackfire, Datadog). Strong candidates mention: using the Doctrine panel to spot N+1 queries, the Security panel to debug voter decisions, profiling specific requests by token, and that the profiler should never be enabled publicly in production.

What interviewers are looking for

Baseline Symfony debugging skill. Candidates who do not use the profiler are debugging blind. Those who can navigate the panels and extract actionable information debug efficiently.

Permalink →
Quick Tip

Match the tool: "QueryBuilder when the query shape varies based on user input. DQL for fixed complex queries with entity hydration. Raw SQL for reporting queries or performance-critical reads where I only need arrays."

What good answers include

DQL (Doctrine Query Language): SQL-like syntax operating on entities instead of tables. Good for complex queries with joins and aggregations. QueryBuilder: programmatic, fluent API for building DQL dynamically — ideal when queries vary based on conditions (filters, search). Raw SQL (via Connection): when you need database-specific features, complex aggregations, or performance-critical queries that bypass ORM hydration. Trade-offs: DQL and QueryBuilder return hydrated entities (convenient but costly for read-only data); raw SQL returns arrays (fast but no entity features). Strong candidates discuss: using SELECT NEW for DTO hydration, partial objects, result caching, and when the ORM abstraction helps versus hinders.

What interviewers are looking for

Tests Doctrine depth. Candidates who only use findBy/findOneBy cannot handle complex data requirements. Those who reach for raw SQL by default are not leveraging the ORM. Look for judgement about when each approach fits.

Permalink →
Quick Tip

Show a complete pipeline: "Deploy to a new release directory, run composer install and cache warmup, run migrations, swap the symlink, then clear OPcache. Shared directories for uploads and var/log persist across releases."

What good answers include

Deployment steps: install dependencies (composer install --no-dev --optimize-autoloader), run migrations (doctrine:migrations:migrate), warm the cache (cache:warmup --env=prod), compile assets (asset-map:compile or encore/webpack), and sync static files. Zero-downtime: deploy to a new directory, run all build steps, then atomically swap the symlink. Tools: Deployer, Ansistrano, or custom scripts. Strong candidates discuss: the importance of APP_ENV=prod and APP_DEBUG=0, using real environment variables instead of .env in production, OPcache preloading, shared directories for uploads and logs across releases, and health check endpoints for load balancers.

What interviewers are looking for

Senior operational question. Candidates who deploy by FTPing files and clearing cache manually will cause downtime. Those who understand atomic deployments, cache warming, and shared resources deploy reliably.

Permalink →
Quick Tip

Lead with pragmatism: "I would not rewrite. I would set up Symfony next to the legacy app, route new features through Symfony, and migrate existing routes one at a time with tests for each. The legacy app shrinks until it disappears."

What good answers include

The Strangler Fig pattern: run Symfony alongside the legacy app and incrementally route requests to the new system. Steps: set up Symfony as a wrapper, use a catch-all controller or reverse proxy to forward unhandled routes to the legacy app, migrate one route at a time, and share the database. Strong candidates discuss: using the Symfony kernel to bootstrap alongside legacy code, sharing sessions between old and new systems, gradual extraction of services from legacy globals into injectable Symfony services, writing tests before migrating each piece, and resisting the rewrite temptation. The key insight is that migration is a series of small, shippable steps, not a big bang.

What interviewers are looking for

Senior architecture question. Candidates who propose a full rewrite are underestimating the risk and business cost. Those who describe incremental migration with the Strangler Fig pattern and test coverage demonstrate mature engineering judgement.

Permalink →
Quick Tip

Cover the essentials: "I define routes with #[Route] attributes on controller methods. Parameters like {id} are passed as method arguments. I use route names everywhere so URLs can change without breaking links."

What good answers include

Symfony matches incoming URLs against a compiled list of route definitions. Routes map a URL pattern and HTTP method to a controller callable. Routes can be defined via PHP attributes (#[Route]), YAML, XML, or PHP configuration. Attributes on controller methods are the modern convention. Key features: route parameters ({slug}), requirements (regex constraints on parameters), defaults, route names for URL generation, and route prefixes on controller classes. The router is compiled and cached — route matching is fast even with hundreds of routes. Strong candidates mention: the debug:router command for listing all routes, generating URLs with the router service or Twig path()/url() functions, and that route priority follows definition order.

What interviewers are looking for

Fundamental Symfony skill. Candidates who cannot explain how a request reaches a controller will struggle with any Symfony project. Ask them to define a route with a parameter and explain how they would generate a link to it in a template.

Permalink →
Quick Tip

Show you understand the reasoning: "Only public/ is web-accessible, so PHP source code cannot be downloaded directly. var/ holds cache and logs — these are environment-specific and never committed. Everything in src/ is autoloaded via PSR-4."

What good answers include

src/: application PHP code — controllers, entities, services, repositories, commands. config/: configuration files — services.yaml, routes, packages, and environment-specific overrides. templates/: Twig template files, organised by controller or feature. public/: the web root — contains index.php (front controller), CSS, JS, and images. Only this directory is web-accessible. var/: generated files — cache, logs, and session storage. Not committed to version control. Additionally: migrations/ for database migrations, tests/ for PHPUnit tests, translations/ for i18n files, and vendor/ for Composer dependencies. Strong candidates explain: why only public/ should be web-accessible (security), that var/ and vendor/ are gitignored, and that the Kernel class in src/ boots the application.

What interviewers are looking for

Baseline Symfony knowledge. Candidates who put application logic outside src/ or who do not understand why public/ is the document root have gaps in project setup fundamentals. Quick to test and reveals whether someone has actually set up a Symfony project.

Permalink →
Quick Tip

Keep it practical: "The controller takes a Request, calls a service for business logic, and returns a Response. I use $this->render() for HTML, $this->json() for APIs, and $this->redirectToRoute() after form submissions."

What good answers include

Controllers are callables (typically methods on classes extending AbstractController) that receive a Request and return a Response. Access request data via the Request object: $request->query->get() for GET parameters, $request->request->get() for POST data, $request->attributes->get() for route parameters. Return responses: return new Response() for raw content, $this->render() for Twig templates, $this->json() for JSON, $this->redirectToRoute() for redirects. AbstractController provides shortcut methods for rendering, redirecting, generating URLs, creating forms, and accessing the service container. Strong candidates mention: that controllers should be thin — business logic belongs in services, type-hinted action parameters for automatic argument resolution, and returning proper HTTP status codes.

What interviewers are looking for

Entry-level Symfony question. Candidates who cannot explain the request-response cycle in a controller need more framework experience. Those who mention keeping controllers thin and delegating to services show good architecture instincts even at a junior level.

Permalink →
Quick Tip

Show the lifecycle: "I define an entity with ORM attributes, persist it with the entity manager, and call flush to write to the database. For retrieval, I use repository methods — findOneBy for single results, custom QueryBuilder methods for complex queries."

What good answers include

Entities are plain PHP classes that Doctrine maps to database tables. Properties map to columns using ORM attributes (#[ORM\Column], #[ORM\Id], etc.). The EntityManager handles persistence: persist() marks new entities for insertion, flush() writes all pending changes to the database. Repositories provide methods for querying: findOneBy(), findBy(), and custom query methods using QueryBuilder or DQL. Symfony generates repositories automatically when you specify repositoryClass in the entity attribute. Strong candidates explain: that entities should not depend on the framework, the difference between persist and flush, that flush is a single database transaction, and the make:entity command for scaffolding.

What interviewers are looking for

Entry-level Doctrine question. Candidates who cannot explain the persist/flush distinction will misuse the entity manager. Those who understand that flush batches all changes into one transaction demonstrate basic ORM awareness.

Permalink →
Quick Tip

Explain the redirect pattern: "After a successful form submission, I add a flash message and redirect. The next page reads and displays the flash. It works because the session survives the redirect, and the flash is cleared after one read."

What good answers include

Flash messages are session-stored messages that persist for exactly one request — they are automatically cleared after being read. Use $this->addFlash(type, message) in controllers to set them, and app.flashes in Twig to display them. Types are typically success, error, warning, or info. They are stored in the session because the common pattern is redirect-after-POST: the controller processes a form, adds a flash, redirects to another page, and that page displays the message. Without session storage, the message would be lost during the redirect. Strong candidates mention: that flashes are consumed on read (reading them clears them), rendering them in a base template so they work site-wide, and that they should not be used for AJAX responses (use JSON response data instead).

What interviewers are looking for

Simple but reveals understanding of the request lifecycle. Candidates who do not know why flashes use the session may not understand the POST-redirect-GET pattern. Those who also mention rendering flashes in a shared base template show practical experience.

Permalink →
Quick Tip

Show practical usage: "I use make:entity to scaffold entities and add fields interactively, make:migration to generate the SQL diff, and make:controller for new endpoints. It saves setup time while following Symfony conventions."

What good answers include

MakerBundle provides interactive code generators: make:entity (entities and repositories), make:controller (controllers with routes), make:form (form types), make:migration (Doctrine migrations), make:command (console commands), make:voter (security voters), make:test (test classes), and more. It generates boilerplate following Symfony conventions, which developers then customise. Benefits: consistent code structure, correct attribute usage, proper namespacing, and faster scaffolding. It is a dev-only dependency. Strong candidates mention: that generated code is a starting point, not finished code, that make:entity can add properties to existing entities interactively, and that understanding what the generators produce is more important than memorising the commands.

What interviewers are looking for

Entry-level question that reveals workflow familiarity. Candidates who manually create every file from scratch may not know the tooling. Those who rely entirely on generators without understanding the output are equally concerning. Look for candidates who use generators as a starting point.

Permalink →
Quick Tip

Show what the defaults give you: "With autowire and autoconfigure, I create a class with type-hinted constructor parameters and Symfony wires it automatically. I only add explicit config when I need to bind a scalar value or choose between multiple implementations of an interface."

What good answers include

services.yaml is the main service configuration file. The default configuration sets autowire: true (automatically inject dependencies by type-hint), autoconfigure: true (automatically apply tags based on interfaces — e.g., classes implementing EventSubscriberInterface are tagged as event subscribers), and uses a resource directive to register all classes under src/ as services, excluding entities and the kernel. This means most classes are automatically available as services with zero configuration. Strong candidates explain: that autowire eliminates the need to manually specify constructor arguments, that autoconfigure replaces manual tag definitions, and when you need explicit service definitions (binding scalar parameters, aliasing interfaces with multiple implementations).

What interviewers are looking for

Fundamental Symfony configuration question. Candidates who do not understand autowire will manually wire every service. Those who understand the three defaults (autowire, autoconfigure, resource) can explain why most Symfony apps need very little service configuration.

Permalink →
Quick Tip

Show the modern default: "AssetMapper for most projects — no build step, no Node.js, and importmaps handle JavaScript dependencies natively. I reach for Encore only when I need Sass compilation, TypeScript, or a full frontend framework like React."

What good answers include

AssetMapper (Symfony 6.3+): maps asset files to public URLs with versioned filenames for cache busting. No build step, no Node.js required — works with native browser ES modules and importmaps. Best for simpler projects and when you want to avoid a JavaScript build toolchain. Webpack Encore: a Symfony wrapper around Webpack. Handles bundling, transpilation, CSS preprocessing (Sass, PostCSS), code splitting, and hot module replacement. More powerful but requires Node.js and a build step. Strong candidates explain: that AssetMapper is now the default recommendation for new Symfony projects, that Encore is still appropriate for complex frontend needs (React, Vue, TypeScript compilation), and that the asset() Twig function handles versioned URLs regardless of which system is used.

What interviewers are looking for

Tests awareness of current Symfony tooling. Candidates who only know Encore or who manually link CSS and JS files in templates are not using modern asset management. Those who understand the trade-off between AssetMapper simplicity and Encore power make informed choices.

Permalink →
Quick Tip

Draw the line clearly: "Parameters for things like pagination defaults or feature names — static, committed, same everywhere. Environment variables for database URLs, API keys, and secrets — different per environment, never committed."

What good answers include

Parameters are defined in services.yaml under the parameters key and are resolved at container compilation time. They are static values baked into the compiled container. Environment variables are read at runtime from the host environment or .env files. In services.yaml, use %parameter_name% for parameters and %env(VAR_NAME)% for environment variables. Access parameters in services via #[Autowire] attribute or constructor injection. In controllers extending AbstractController, use $this->getParameter(). Use parameters for: non-sensitive configuration that does not change between deployments. Use environment variables for: secrets (API keys, database credentials), values that differ between environments, and anything that should not be committed to version control. Strong candidates mention: env var processors (env(bool:), env(int:), env(json:)) for type casting.

What interviewers are looking for

Entry-level configuration question. Candidates who put secrets in parameters or who do not understand the compilation-time versus runtime distinction will mishandle configuration. Those who know when to use each and can explain env var processors handle configuration correctly.

Permalink →
Quick Tip

Show the developer experience: "I run composer require mailer and Flex creates the config file, adds MAILER_DSN to .env, and registers the bundle. No manual setup. That is the difference — Flex turns package installation into project configuration."

What good answers include

Symfony Flex is a Composer plugin that automates bundle configuration. When you install a package, Flex checks for a recipe — a set of instructions that creates config files, adds environment variables to .env, registers bundles, and creates boilerplate directories. This means installing a package like symfony/mailer automatically adds the config file, sets up MAILER_DSN in .env, and registers the bundle — zero manual setup. Recipes are stored in two repositories: the official symfony/recipes and the community contrib repository. Strong candidates explain: that Flex aliases allow shorthand (composer require mailer instead of the full package name), the symfony.lock file tracks which recipes have been applied, and that recipes can be updated with composer recipes:update.

What interviewers are looking for

Entry-level Symfony ecosystem question. Candidates who manually create config files for every bundle may not know about Flex. Those who understand how recipes automate setup appreciate why Symfony projects have minimal boilerplate out of the box.

Permalink →
Quick Tip

Show the pattern: I tag services with AutoconfigureTag and inject them with TaggedIterator. For example, I tag all report generators and inject the collection into a ReportManager that iterates and delegates. Autoconfigure handles built-in tags like event subscribers automatically.

What good answers include

Tagged services let you group related services under a label and inject them as a collection. You tag services in services.yaml or via #[AutoconfigureTag] and #[TaggedIterator] attributes. The container collects all services with a given tag and injects them — typically into a service that iterates over them. Classic examples: Twig extensions (twig.extension), event subscribers (kernel.event_subscriber), and custom plugin systems. Compiler passes can also process tags for advanced scenarios. Strong candidates explain: that autoconfigure handles common tags automatically, how to define priority on tags, and when you would use a tagged locator (lazy loading) versus a tagged iterator (eager loading).

What interviewers are looking for

Tests understanding of the DI container beyond basic autowiring. Candidates who manually wire every dependency instead of using tags are creating rigid, hard-to-extend code. Those who understand tagged iterators and locators can build plugin-style architectures.

Permalink →
Quick Tip

I define scoped clients in config for each external API. In tests, I inject MockHttpClient with predefined MockResponse objects — no network calls, deterministic results. In dev, I check the HttpClient profiler panel for request details.

What good answers include

Symfony HttpClient provides a PSR-18 compatible client with scoping (base_uri per named client), automatic JSON decoding, retry on failure, and async capabilities. Configure named clients in framework.yaml under http_client.scoped_clients. For testing: use MockHttpClient with MockResponse to simulate API responses without hitting real endpoints. Strong candidates discuss: the difference between streaming and buffered responses, using the retry_failed option with custom strategies, ScopedHttpClient for API-specific configuration, and CachingHttpClient for reducing external calls.

What interviewers are looking for

Tests how candidates handle external integrations. Those who use raw cURL or Guzzle in Symfony miss the framework integration. Those who cannot explain how to test external API calls will write untestable, fragile code.

Permalink →
Quick Tip

I use a state machine for order status — placed, paid, shipped, delivered. Transitions are guarded: you cannot ship until paid. Guard listeners check business rules. The profiler shows the visual graph of all possible transitions.

What good answers include

The Workflow component models business processes with places (states) and transitions (allowed state changes). A state machine allows only one current place; a workflow allows multiple concurrent places. Configuration lives in config/packages/workflow.yaml. Use cases: order processing (draft to submitted to approved to shipped), content moderation (pending to reviewed to published), and any domain with defined state transitions. Events fire on each transition (guard, enter, leave, completed) allowing you to hook in business logic. Strong candidates explain: using guard events to block transitions based on conditions, storing the state on the entity via a marking_store, and the audit trail feature for compliance.

What interviewers are looking for

Tests whether candidates reach for framework tools versus reinventing state management. Those who use if/else chains for complex state logic create unmaintainable code. The Workflow component provides validation, events, and visual debugging.

Permalink →
Quick Tip

I define translation keys in YAML files by domain. Twig templates use the trans filter. I run translation:extract to find any untranslated keys. The locale comes from the URL prefix and falls back to the default locale in config.

What good answers include

Translations live in translations/ as YAML, XLIFF, or PHP files, named by domain and locale (messages.en.yaml, validators.fr.yaml). In Twig: use the trans filter or trans block. In PHP: inject TranslatorInterface and call trans(). The locale is set per request — typically from the URL, session, or Accept-Language header. ICU message format handles pluralisation and complex rules. Strong candidates discuss: translation domains for organising large projects, the translation:extract command for finding missing keys, fallback locales, and how to handle locale switching in routes with _locale parameter.

What interviewers are looking for

Tests readiness for international projects. Candidates who hardcode strings in templates or use ad-hoc translation solutions will struggle when the project needs a second language. Those who understand domains and ICU format handle complex translation requirements cleanly.

Permalink →
Quick Tip

I create a UniqueEmail constraint and validator. The validator injects the UserRepository and checks if the email already exists. I apply it to the entity with the UniqueEmail attribute and it works in forms and API validation automatically.

What good answers include

A custom constraint needs two classes: a Constraint class (defines the annotation/attribute and error message) and a ConstraintValidator class (contains the validation logic). The constraint is applied to entity properties or form fields via attributes. When validation runs, the framework maps the constraint to its validator, calls validate(), and the validator adds violations via the execution context. Strong candidates explain: class-level constraints for multi-field validation, using dependency injection in validators (e.g., injecting a repository to check uniqueness), the Payload property for severity levels, and constraint groups for applying different validation rules in different contexts.

What interviewers are looking for

Tests practical validation experience. Candidates who cram all validation into controllers or form event listeners miss the reusable constraint system. Those who can inject services into validators and use constraint groups handle real-world validation complexity.

Permalink →
Quick Tip

For JSON APIs, I use DTOs with MapRequestPayload — cleaner than forms, automatic validation, typed properties. For HTML forms, I still use the form system because it handles rendering, CSRF, and data transformation.

What good answers include

MapRequestPayload (Symfony 6.3+) maps JSON request bodies directly to typed DTOs using the Serializer and Validator. MapQueryString does the same for query parameters. This replaces manual request get calls and provides automatic validation and type safety. DTOs are plain PHP objects with typed properties and validation attributes. Compared to forms: DTOs with MapRequestPayload are simpler for API endpoints, while forms are better for HTML form rendering with CSRF protection. Strong candidates discuss: using validation groups on DTOs, handling nested objects, custom denormalization for complex types, and when forms are still the better choice (HTML rendering, data transformers, CSRF).

What interviewers are looking for

Tests awareness of modern Symfony. Candidates who manually deserialise JSON in every controller action are doing unnecessary work. Those who understand the DTO versus form trade-off choose the right tool for API versus HTML contexts.

Permalink →
Quick Tip

In prod I use a fingers_crossed handler — it buffers debug-level logs silently, but if an error occurs, it flushes the entire buffer. That gives me full context around errors without logging everything all the time.

What good answers include

Symfony uses Monolog via the MonologBundle. Handlers define where logs go (file, stderr, Slack, Elasticsearch). Channels group log messages by topic (doctrine, security, app, custom). In dev: log everything to a file with a low threshold. In production: stream errors to stderr, send critical alerts to Slack or email, and rotate file logs. Config lives in config/packages/monolog.yaml, split by environment. Strong candidates explain: creating custom channels for domain-specific logging, using fingers_crossed handler to capture debug logs only when an error occurs, and the difference between bubble true and false for handler chaining.

What interviewers are looking for

Tests operational awareness. Candidates who use error_log or dump in production have no structured logging strategy. Those who understand channels, handlers, and the fingers_crossed pattern can debug production issues efficiently.

Permalink →
Quick Tip

I decorate the ProductRepository with a CachedProductRepository. The decorator checks Redis first, falls back to the inner repository, and caches the result. Every service that injects ProductRepository automatically gets the cached version — no changes needed.

What good answers include

Service decoration wraps an existing service with a new implementation while preserving the original. The decorator implements the same interface, receives the inner (decorated) service via injection, and delegates to it while adding behaviour. Configured with the AsDecorator attribute or decorates key in services.yaml. Use cases: adding caching around a repository, logging around an API client, or modifying behaviour of third-party bundle services without touching their code. Unlike inheritance: decoration works at the container level and the original service is unaware. Unlike plain composition: the container swaps the service ID so all consumers get the decorator automatically. Strong candidates discuss: decoration priority when multiple decorators exist, accessing the inner service with MapDecorated, and that this is the open/closed principle in practice.

What interviewers are looking for

Tests advanced DI knowledge. Candidates who modify third-party services by forking or monkey-patching do not know about decoration. Those who use it demonstrate understanding of SOLID principles and the Symfony container.

Permalink →
Quick Tip

Stimulus keeps JavaScript minimal — each controller handles one behaviour. I add data-controller to HTML and the JavaScript activates automatically. With Turbo, most pages feel like an SPA without writing frontend framework code.

What good answers include

Symfony UX is a set of PHP packages that integrate JavaScript libraries via Stimulus controllers. Stimulus is a lightweight JS framework that connects HTML elements to controller classes using data attributes (data-controller, data-action, data-target). Symfony UX packages (Turbo, Chartjs, Autocomplete, Live Component, etc.) provide pre-built Stimulus controllers installable via Composer. AssetMapper or Encore loads the controllers. The key concept: server-rendered HTML drives the behaviour — no client-side routing or virtual DOM. Strong candidates explain: the naming convention between controller filenames and data-controller values, Stimulus values and targets for managing state, how Turbo frames enable partial page updates without writing JavaScript, and when UX Live Components replace the need for a JavaScript framework.

What interviewers are looking for

Tests frontend strategy in Symfony. Candidates who default to React or Vue for every interactive feature may not know that Symfony UX handles common patterns with less complexity. Those who understand Stimulus and Turbo can build interactive applications with minimal JavaScript.

Permalink →
Quick Tip

I use URL versioning for clarity: /v1/ and /v2/ with separate controllers. Within a version, I evolve by adding optional fields and using serialization groups. Deprecated endpoints return a Sunset header with a removal date.

What good answers include

Versioning strategies: URL prefix (/v1/, /v2/), custom header (X-API-Version), or Accept header media type (application/vnd.app.v2+json). URL prefix is simplest and most cacheable. Content negotiation uses the Accept header to determine response format (JSON, XML). In Symfony, use MapRequestPayload with different DTOs per version, or use serialization groups to control which fields appear per version. Strong candidates discuss: maintaining backwards compatibility by only adding fields (never removing), using deprecation headers to warn clients, the Symfony Serializer groups for version-specific field sets, running old and new versions in parallel during migration, and API documentation tools like NelmioApiDocBundle that document versions separately.

What interviewers are looking for

Tests API design maturity. Candidates who break API contracts without versioning cause client outages. Those who understand versioning strategies and deprecation workflows can evolve APIs safely.

Permalink →
← All Software Developer questions