Laravel in Berlin
Laravel development for Berlin teams
Laravel and PHP engineering for Berlin product teams - an hour of time difference, your own engineers in the pull requests, and deletion and export duties that land in the schema.
A Berlin product company usually has engineers before it has a backend problem, and that ordering decides what an engagement here looks like. Nobody is buying a specification taken away and returned in three months, because there is already a team who would have to live with the result.
What arrives instead is a named problem - a queue that loses jobs, a schema that stopped describing the business, an authorisation rule that only holds in the controller - and a request to fix it in the repository where it lives.
Seven hours of overlap with Berlin
Berlin is one hour behind Ankara. There is no part of your working day we are not reachable for, which matters most for the work that genuinely cannot be done asynchronously.
A migration against a forty-million-row table is agreed on a call and watched by both sides. A worker that starts behaving differently after a deploy gets diagnosed while somebody who knows what the data means is still at their desk. With six to eight hours of difference each of those becomes a next-day item, and a backend fault that waits a day usually costs more than the repair.
What a Berlin codebase usually needs
The gaps that recur in Berlin codebases are consistent enough to name.
The queue is the most common. Jobs that are not safe to run twice, in a system that retries by design - which means there is already a duplicate email, a duplicate charge or a duplicate webhook somewhere that nobody has traced.
Next is the schema that described the business two pivots ago. Columns nobody writes to, a status enum with values that mean different things depending on when the row was created, and a JSON column that became the place new fields go because adding a migration felt heavy. Each addition was reasonable; the sum is a model nobody can reason about.
Third is authorisation applied in controllers rather than at the data. It works until something reaches the records by another path - an export, a scheduled report, an internal tool - and those are exactly the paths that leak. Authorisation is not authentication, and the difference shows the first time an endpoint is called directly.
Data protection is a Laravel schema decision
The German obligations that matter to a backend are not the ones on the privacy page.
Deletion has to delete. A deleted_at column is a filter, and the row remains readable by anything that forgets the scope. It is also rarely the only copy - the same personal data sits in the search index, in an analytics export, in last night's backup and in a CSV somebody generated for a meeting.
Export has to be assemblable. Producing everything held about one person is only possible if you know where it all lives, which is a question about the data model rather than about a form.
And consent is a record with a version and a timestamp, not a boolean. The question asked later is never "did they agree" but "to what, and when, and under which wording".
None of these are difficult to build at the start. All are expensive once there are three years of rows.
In Berlin the works council has a say in what the system records
An internal tool that touches how people work is not purely a technical decision in Germany. Where a works council exists, a system capable of monitoring staff performance or conduct is subject to co-determination, and that is not a formality to be settled after launch.
The practical consequence is that what the application records about its own users becomes an explicit design question rather than a side effect. A timestamp on every action, a log of who viewed which screen, a dashboard showing throughput per person - each of those may be perfectly reasonable and each has to be justified, bounded and agreed rather than built because it was easy.
What that looks like in the schema is narrower logging with a stated purpose and a retention period, aggregates where individual attribution is not needed, and the ability to switch a particular collection off without unpicking the application. A system designed on the assumption that everything is logged forever cannot be adjusted when the agreement says otherwise; one where each collection is a deliberate, separable decision can.
Engaging early is cheaper, and this is the part worth saying plainly to a project sponsor. A feature built, demonstrated and then withdrawn because the council objected costs the build twice. The same conversation held while the feature is a paragraph costs nothing.
A Berlin application that got slow gradually
Nothing broke. The application is simply slower than it was eighteen months ago, and nobody can point at the change that did it, because there was no change - there was growth.
The diagnosis has an order and skipping it wastes weeks. Measure first, in production, with per-query timing and a view of what the slowest requests actually spend their time on. The intuition about which page is slow is right about half the time, and the cause is almost never where the team expected.
What it usually is, in a Laravel application of that age, is a list view issuing one query per row. It was invisible at a thousand rows and is fatal at four hundred thousand, and it is produced by exactly the convenience that makes Eloquent pleasant to write. Eager loading fixes it in an afternoon once it has been found, and finding it reliably means a tool that flags the pattern in tests rather than a developer noticing.
Second most common is an index that was never added, because the query was fast on the development machine's dataset. Third is a count query on a table that has grown past the point where counting is cheap, run on every page load to render a pagination control nobody uses.
None of that is a rewrite, and the instinct to treat gradual slowness as evidence that the architecture is wrong is expensive and usually mistaken. The measurement is what distinguishes the two, and it costs a day.
Working alongside the engineers who inherit it
A buyer with a team rules out the arrangement most agencies sell: a parallel repository, a parallel process, and a handover event at the end of it.
What replaces it is unremarkable, and that is the point. Your branch policy. Your review process. Your CI. Your engineers disagreeing with our pull requests in week one, while disagreement is still cheap.
Month one is slower that way. It is also the only version where the queue we fixed is still working a year later, because the people maintaining it watched it being built.
The country-level ground - the e-invoicing format, hosting decisions, what German data protection expects of a supplier - sits on the Germany market page.
