When I started rebuilding EvolvePHP, I knew one thing very early:
I did not want to build another PHP framework simply because I could.
PHP already has Laravel, Symfony, CakePHP, Yii and several other mature frameworks. They have large communities, years of development behind them, extensive documentation and ecosystems that EvolvePHP cannot realistically compete with by simply offering another router, ORM, service container or authentication system.
So the question I kept asking myself was:
Why should EvolvePHP 2 exist?
The answer I keep coming back to is change.
Software changes.
Businesses change.
Requirements change.
Teams change.
Infrastructure changes.
And sometimes the framework or architecture you chose five or ten years ago is no longer the architecture your application needs today.
That is where I want EvolvePHP 2 to focus.
Applications rarely remain what we originally designed
A lot of applications start small.
Maybe you are building a SaaS product with users, subscriptions, notifications and reports.
At the beginning, putting everything inside one application makes perfect sense.
You don't need twenty services.
You don't need Kubernetes.
You probably don't need a complicated event-driven architecture.
You need to ship.
But if the application succeeds, things start changing.
The reporting system becomes expensive to run.
Notifications begin processing millions of messages.
A certain part of the system needs to scale differently.
The business expands into another country.
Another development team takes ownership of one part of the application.
A third-party integration becomes critical.
Something that originally looked like this:
Application
├── Users
├── Billing
├── Orders
├── Notifications
└── Reports
may eventually need to become something different.
The problem is that many architectural decisions become very expensive to undo later.
EvolvePHP 2 is being designed around the idea that we should expect this change instead of pretending it will not happen.
Start with a modular monolith
I am not interested in encouraging developers to start every new application with microservices.
In many cases, I think that creates unnecessary complexity.
A new EvolvePHP application should be able to begin as a normal application deployed as one unit.
But internally, I want the application to encourage clear boundaries.
Instead of treating the whole application as one large collection of controllers, models and services, the system can be organised around capabilities:
Application
├── Identity
├── Billing
├── Orders
├── Notifications
└── Reporting
Each module should have a clear responsibility.
Its dependencies should be explicit.
Its contracts should be understandable.
The application can still be one deployable system.
That gives you the simplicity of a monolith without giving up every architectural boundary inside it.
Then, if the business reaches a point where Reporting needs to run separately, the goal is not to redesign the entire application just to make that possible.
The architecture should already give us somewhere to begin.
That is what I mean when I say EvolvePHP is being built for change.
I want deployment architecture to be able to evolve
One of the long-term goals behind the framework is that a module should not be unnecessarily tied to where it currently runs.
A module may begin embedded inside the application.
Later, that same business capability may need to run inside a worker.
Eventually it may need to become a separate service.
Conceptually:
Today
Application
├── Billing
├── Orders
└── Reporting
could become:
Later
Application
├── Billing
└── Orders
|
|
v
Reporting Service
This does not mean moving a module remotely will magically require zero engineering work.
Distributed systems introduce networking, failure handling, latency, retries, idempotency, security and many other concerns.
Pretending otherwise would be misleading.
What I want EvolvePHP to do is reduce the amount of business architecture that must be thrown away when that transition happens.
The framework should help you create boundaries early enough that future change is possible.
Existing applications also need a path forward
The idea of building for change does not only apply to new applications.
There is another problem I care about just as much: existing PHP systems.
There are applications running today that were written many years ago and still make money for businesses.
Some are custom PHP applications.
Some use older versions of CodeIgniter, Laravel, Symfony, CakePHP, Yii or other frameworks.
Some were designed before containers, cloud deployment, persistent workers and modern observability were normal considerations.
The easy advice is:
Rewrite it.
The practical reality is very different.
A ten-year-old business application may contain years of business rules that nobody has documented properly.
It may have hundreds of database tables.
It may communicate with government systems, banks, payment providers or internal services.
Customers may depend on it every day.
A complete rewrite might take years, and the business still has to continue changing while the rewrite is happening.
So instead of making EvolvePHP 2 require an all-or-nothing migration, I am designing Evolve Bridge around incremental adoption.
The goal is to make something like this possible:
Existing Application
|
|
Evolve Bridge
|
|
New Evolve Module
The existing application does not immediately disappear.
You modernise one capability.
Then another.
And another, if it makes sense.
Maybe the old application is eventually replaced completely.
Maybe it isn't.
The important part is that adopting EvolvePHP should not require the business to gamble everything on one massive rewrite.
Even EvolvePHP itself should not become a trap
There is another side to this.
If I say EvolvePHP helps developers escape architectural traps created by older systems, then EvolvePHP itself should not become another trap.
That means I have to think carefully about framework boundaries.
Application business logic should not need to know unnecessary details about framework internals.
Modules should not be able to depend on anything they want.
Plugins and application modules should not mean the same thing.
Package dependencies should remain directional.
Public contracts should be intentional.
These things may sound restrictive when you are building something quickly.
But years later, those boundaries are often what determine whether a system is easy or painful to change.
I learned some of this from EvolvePHP 1.
When you maintain software long enough, you start appreciating decisions that make tomorrow's work easier.
Runtime assumptions also change
PHP itself is changing.
Traditionally, PHP applications had a very convenient lifecycle:
Request starts
↓
Application runs
↓
Response returned
↓
Process state disappears
That model hides many mistakes.
If a developer accidentally stores request-specific information in static state, it often disappears at the end of the request anyway.
With persistent workers, that assumption changes.
The same process may handle another request.
Or another queue message.
Or another task.
State from one execution can accidentally survive into the next.
That becomes much more serious when the leaked information contains:
the current user,
tenant information,
database transaction state,
listeners,
locale,
authentication context,
logging context,
or other execution-specific data.
So EvolvePHP 2 does not treat persistent-worker safety as something that can simply be added later by putting the framework behind a fast server.
The framework is being designed around explicit service lifetimes and isolated executions.
At the foundational level, I currently think about services as:
Application
Execution
Transient
An application service can live for the application lifecycle.
An execution service belongs to one request, job, command or task.
A transient service can be created when needed.
More importantly, when an execution ends, the framework needs to be able to clean up that execution deterministically.
If cleanup fails and the framework cannot prove the process is safe for another execution, the correct response should not be:
Hopefully it is fine.
The process should be treated as unsafe for reuse.
This is the idea behind the quarantine model being developed in EvolvePHP 2.
HTTP is not the whole application
Another change in my thinking is that I don't want the entire framework architecture to assume that everything is an HTTP request.
Modern applications do much more than HTTP.
They process:
web requests,
queue messages,
scheduled jobs,
CLI commands,
background worker tasks.
So internally, EvolvePHP is developing the broader concept of an execution.
An HTTP request is an execution.
A queue message is an execution.
A scheduled job is an execution.
A command is an execution.
They have different inputs and outputs, but they share important lifecycle concerns:
Execution starts
↓
Context created
↓
Application work
↓
Cleanup
↓
Isolation verified
↓
Execution ends
This gives the framework a more consistent foundation for different runtime environments without pretending those environments are identical.
Observability should be part of the architecture
Another thing that becomes important as applications grow is understanding what they are actually doing.
When a request becomes slow, I want developers to be able to answer:
Which module handled it?
What services were involved?
How many database operations happened?
Which external calls were made?
Where was the time spent?
What happened during cleanup?
Did the execution end safely?
This is why EvolvePHP has two observability directions in its architecture.
Evolve Insight is intended for local and development diagnostics.
Evolve Observe is intended for production telemetry and OpenTelemetry integration.
They are different tools serving different environments, but both can eventually benefit from the same framework instrumentation.
Again, this is not about trying to rebuild Grafana, Datadog or an OpenTelemetry backend.
The framework should produce useful information and integrate with the tools that already exist.
Understanding change before making it
The modernization direction has also led to another idea I am increasingly interested in: EvolvePHP should eventually help developers understand an existing system before asking them to change it.
That is the thinking behind Evolve Audit and Evolve Doctor.
Audit is intended to answer questions such as:
What exactly am I dealing with?
It could eventually inspect areas like:
PHP version risk,
dependency health,
framework lifecycle,
global and static state,
application coupling,
possible module boundaries,
persistent-runtime risks,
modernization candidates.
Doctor has a different job:
Is this application correctly configured and safe for the environment I want to run it in?
Those tools are not the framework today; they are part of the direction being designed for later phases.
But they fit the same philosophy.
Before changing a system, understand it.
Before upgrading it, know what could break.
Before putting it inside a persistent runtime, determine whether its state is actually isolated.
This is why I say "built for change"
When I describe EvolvePHP 2 as a PHP framework built for change, I don't mean that every future architectural change will become automatic.
Software doesn't work that way.
There will still be difficult migrations.
There will still be bad architectural decisions.
There will still be systems that need major redesigns.
What I want to provide is a better starting position.
For a new application:
Start modular, without starting distributed.
For an existing application:
Modernise incrementally, without requiring a full rewrite.
For a growing application:
Extract capabilities when the business actually needs it.
For runtime changes:
Treat isolation and cleanup as architectural requirements.
For operations:
Make the system observable enough to understand what it is doing.
And for upgrades:
Reduce uncertainty before making the change.
That is the direction.
EvolvePHP 2 is still being built
I think it is important to say this clearly.
EvolvePHP 2 is under active development.
Some of what I have described here represents architecture that has already been defined and foundational work that is being implemented.
Other parts, particularly the broader modernization and adoption tooling, belong to later stages of the roadmap.
I would rather document the thinking openly than wait until everything is finished and pretend the final architecture appeared fully formed.
Some decisions may still improve as implementation provides evidence.
That is part of building software too.
EvolvePHP 1 evolved because I kept using it on real projects and learning from it.
EvolvePHP 2 is being approached more deliberately, but I still expect implementation, testing and real-world use to challenge some assumptions.
And when that happens, the framework should be willing to evolve.
After all, that is the entire point.
EvolvePHP 2 is not being built around the assumption that software stays the same.
It is being built around the reality that good software has to survive change.

No comments:
Post a Comment