Stop Over-Engineering Everything
Modern web development is suffocating under layers of self-inflicted abstraction.
Whether it is an "enterprise" PHP framework that requires fifty classes just to output a "Hello World" message, or a modern JavaScript stack that ships two megabytes of code to render a static blog post, we have lost sight of what software is actually supposed to do. We have traded runtime performance and basic maintainability for dogmatic "best practices" that make our code slower, bloated, and incredibly hard to reason about.
Pragma CMS was built as a deliberate step backward toward sanity. We value code readability, execution speed, and mental traceability over design-pattern compliance.
Most modern PHP frameworks implement a version of Model-View-Controller (MVC) that feels less like a structure and more like a maze. Finding how a single query is resolved requires jumping through controllers, service providers, interfaces, repositories, and dependency injectors. This constant context-switching increases cognitive load and slows down development.
In Pragma CMS, we use a simplified MVC-Lite pattern. Business logic resides in straightforward Managers or Controllers, and data is passed directly to the view templates without hidden middleware chains or magic abstractions. If you want to know where a piece of data comes from, you read the controller. If you want to know how it is rendered, you read the template.
Consider the contrast between a typical enterprise-style controller and Pragma's direct execution path.
class HomepageController
{
public function __construct(
private SeoService $seoService,
private ArticleRepository $articleRepository,
private ThemeRenderer $renderer,
private RouteContext $routeContext
) {}
public function index(): Response
{
try {
$route = $this->routeContext->getCurrentRoute();
$page = $this->seoService->resolvePage($route);
$articles = $this->articleRepository->findLatestPublished(3);
return $this->renderer->render('home.twig', [
'page' => $page,
'articles' => $articles
]);
} catch (Exception $e) {
throw new HttpNotFoundException();
}
}
}
$routeHandle = $page->currentRoute['handle'];
$pageData = PageManager::getPageDataForRoute($routeHandle);
if (!$pageData) {
displayError("The page does not exist", 404);
exit;
}
$page->viewData["articles"] = EntryManager::getEntries(
"article",
$_SESSION["lang_id"],
["limit" => 3]
);
echo render_template("base.php", $page);
Both approaches yield the exact same output, but Pragma keeps the call stack shallow, the code explicit, and the execution path entirely visible.
Object-Oriented Programming (OOP) is a tool for organizing code, not a dogma. Somewhere along the line, the industry decided that every single database row, configuration value, or request parameter must be wrapped in a dedicated object. This has led to deep inheritance trees, proxy objects, and design-pattern bloat.
Following the principles of data-oriented design, Pragma CMS focuses on the data itself rather than pure object-oriented abstraction. An article in a CMS is fundamentally a row in a database table. It does not need to be an instance of a complex class with virtual getters, setters, and lazy-loading decorators.
By treating content as clean, structured data (flat associative arrays or JSON objects), we drastically reduce memory allocations, simplify debugging, and keep execution times remarkably low.
$article = $articleRepository->find($id);
$article->getMetadata()->getSeo()->getTitle();
$article = EntryManager::getEntry("article", $id);
$title = $article["title"];
Because there are no hidden proxy objects or deep method chains, you can easily print, dump, or serialize the data at any point in the execution cycle.
Modern frameworks insert multiple layers between your code and your database: repositories, query builders, ORM entities, lazy-loaded relations, and unit-of-work engines. While these abstractions claim to protect you from writing SQL, they actually obscure what is happening at the database level.
By the time the database receives a query, the application has allocated dozens of temporary objects simply to construct a string. This makes slow queries difficult to diagnose and optimize.
Pragma CMS keeps the database layer direct and explicit. We write standard, optimized SQL queries that run immediately through a secure, prepared PDO wrapper.
This is typical "enterprise" database access, where the actual SQL query is generated behind several layers of classes:
class ArticleService
{
public function __construct(private ArticleRepository $repository) {}
public function getLatestArticles(): array
{
return $this->repository->findLatestPublished(3);
}
}
class ArticleRepository
{
public function __construct(private EntityManager $em) {}
public function findLatestPublished(int $limit): array
{
return $this->em->getRepository(Article::class)
->createQueryBuilder('a')
->where('a.status = :status')
->setParameter('status', 'published')
->orderBy('a.createdAt', 'DESC')
->setMaxResults($limit)
->getQuery()
->getResult();
}
}
We keep the data flow visible, utilizing a simple SQL statement with prepared parameters:
$articles = Database::fetchAll(
"SELECT * FROM entries
WHERE type = :type
AND status = :status
ORDER BY created_at DESC
LIMIT :limit",
[
"type" => "article",
"status" => "published",
"limit" => 3
]
);
For more complex, dynamic requirements, we wrap the query construction within thin, static managers that still output standard SQL without hiding the database footprint:
class EntryManager
{
public static function getEntries(
string $contentTypeHandle,
?int $langId = null,
array $options = []
) {
$contentType = ContentTypeManager::get($contentTypeHandle);
if (!$contentType) {
return null;
}
$langId = $langId ?? $_SESSION['lang_id'];
// ----------------------------
// JOINS
// ----------------------------
$joins = implode(' ', $options['joins'] ?? []);
// ----------------------------
// WHERE CLAUSES (base)
// ----------------------------
$whereClauses = [
"e.status = 1",
"e.content_type_handle = :content_type_handle",
"(e.published_at IS NULL OR e.published_at <= NOW())"
];
$params = $options['params'] ?? [];
$params['content_type_handle'] = $contentTypeHandle;
$params['lang_id'] = $langId;
// WHERE dynamiques (extension)
if (!empty($options['where_clauses'])) {
$whereClauses = array_merge($whereClauses, $options['where_clauses']);
}
$whereSql = "WHERE " . implode(" AND ", $whereClauses);
// ----------------------------
// ORDER / LIMIT / OFFSET
// ----------------------------
$orderBy = $options['order_by'] ?? "e.created_at DESC";
$limit = isset($options['limit'])
? "LIMIT " . (int)$options['limit']
: "";
$offset = isset($options['offset'])
? "OFFSET " . (int)$options['offset']
: "";
// ----------------------------
// QUERY BUILD
// ----------------------------
$query =
"SELECT e.*
FROM entries e
{$joins}
{$whereSql}
ORDER BY {$orderBy}
{$limit}
{$offset}";
return Database::fetchAll($query, $params);
}
}
This execution model eliminates automated query generation, hydration overhead, and nested abstraction stacks. You are left with pure, predictable SQL.
This matters because the goal of pragmatism in software engineering is not simply to write "less code", it is to preserve absolute mental traceability between your intent and the system's execution.
When a database operation fails or performance degrades, you should never have to debug ORM internals, proxy objects, repository chains, or nested service layers. You should be able to see immediately:
This level of visibility is what keeps a database footprint fast, predictable, and maintainable over years of production.
The industry has grown overly reliant on try/catch blocks. We throw exceptions for things that aren't actually "exceptional", they are just normal control flow. This makes code unpredictable and hides the actual logic of the application.
In Pragma CMS, we prefer explicit checks. We believe that your code should handle expected failures (like a missing file or a failed DB connection) as part of its regular logic, not as an afterthought in a catch block. This leads to code that is "boring" in the best way possible: it’s predictable, reliable, and easy to trace.
try {
$cache->write($data);
} catch (CacheException $e) {
$logger->error($e);
}
We perform explicit checks at each critical point of execution:
if (!is_dir($cacheDir) && !mkdir($cacheDir, 0755, true)) {
logError("Unable to create cache folder: {$cacheDir}");
return false;
}
if (file_put_contents($tempFile, $content) === false) {
logError("Unable to write cache file: {$tempFile}");
return false;
}
if (!rename($tempFile, $cacheFile)) {
logError("Unable to rename cache file: {$cacheFile}");
@unlink($tempFile);
return false;
}
We believe that the browser is already a powerful platform. Modern web development often adds unnecessary layers between developers and the browser.
Pragma CMS uses Vanilla JavaScript.
We did not build Pragma CMS to win a feature checklist war. We built it for developers who want to understand exactly how their application executes. By stripping away redundant layers of indirection and returning to a clean, well-structured monolithic architecture, we have built a system that remains exceptionally fast and easy to maintain.
Software does not need to be complex to be powerful. It just needs to be pragmatic.