EYThe LogEmre Yakut
← all entries
Systems · Field

I wrote an admin panel without a framework

A server was needed to issue licences to field terminals. Instead of installing a framework and pulling 40 MB of dependencies I wrote six classes. Under 900 lines, and the same code still runs.

private/ — web sunucusu ERİŞEMEZconfig/ → veritabanı bilgilericore/ → altı sınıfcontrollers/views/storage/ → kullanıcılar, logwww/ — WEB KÖKÜindex.php → tek giriş noktası.htaccess → her şey index.php’yeassets/yanlış yapılandırmada bileconfig dosyası servis edilemez900 satır · altı sınıf · beş sayfa — framework yokeski API sözleşmesi BİREBİR korundu: sahadaki cihazlar güncellenemiyor, tek alan bile değişmedi

There are reading terminals in the field. Each one asks on start-up: “am I allowed to run?” — is the licence valid, what permissions does it carry, until when.

A server was needed to answer that. Plus a panel showing who uses which terminal and what each device did when.

Why I did not use a framework

My first reflex was to install one. Then I looked at the requirement list:

  • Five pages: login, dashboard, terminals, logs, users
  • Two tables (both already existed)
  • One API endpoint
  • One role: administrator

Downloading 40 MB of dependencies, learning a configuration ecosystem and handling a major upgrade every year felt out of proportion.

I wrote it myself. Six classes:

App       application shell, container
Router    path → controller
Request   input, headers, body
Response  output, headers, redirects
Db        PDO wrapper (prepared statements)
View      template inclusion, escaping
Auth      session, password verification

Under 900 lines in total. Exactly as much as I needed — no more, no less.

I do not argue this everywhere

A framework’s real value is not saved code, it is a shared vocabulary. With five people on a team, everyone looking at the same pattern beats a bespoke six-class shell by a mile. But here the team is one person and the scope is fixed. In that case a framework is not a convenience, it is a dependency.

Everything secret lives outside the web root

The folder layout has one rule and everything follows from it:

service.bzt/
  private/          ← the web server CANNOT reach this
    config/         configuration, database credentials
    core/           the six classes
    controllers/
    views/
    storage/        users, logs
  www/              ← this is the web root
    index.php       single entry point
    .htaccess       route everything to index.php
    assets/

The value of that split: if one day .htaccess stops working, or a configuration error slips in, an attacker still cannot reach the configuration file — because it is not even inside the tree the web server can see.

The most common leak I see on shared hosting is precisely this: a config.php served because of a misconfiguration.

The old contract kept byte for byte

This service replaces an existing API. Terminals in the field expect that API and cannot be updated — some are in vehicles, some in basements.

So the rule was clear: not a single field of the response may change.

GET /terminal/api/?op=get&p=license&key={hardware}&ver={version}

{
  "status": …,
  "v2":     { "securekey": …  },   ← key derived per month
  "time":   { "license_expire": … },
  "perm":   { "read": …, "edit": … },
  "admin":  { … },
  "security": { "manuplationlock": … }
}

The inside was rewritten completely. The outside — field names, typos included — stands as it was.

the cost and the return of that decision

Cost: some field names make no sense and stay that way. A new developer will look at them and ask why.

Return: not one device in the field stopped during the transition. The new server went up, the old address was pointed at it, nobody noticed.

When replacing a system that is already running in the field, an invisible transition is worth more than a clean API.

And the console theme

The panel’s look is a deliberate choice: dark ground, monospaced type, terminal feel.

Not for aesthetics. The person using this panel is technical and is diagnosing a field problem. Instead of cards and coloured tiles they want a dense, readable list. A terminal aesthetic gives that naturally: many rows per screen, hierarchy carried by typography.

What I learned

“Which framework?” is usually asked too early. The first question should be: how many screens will this system have in five years?

If the answer is five, the scaffolding ends up larger than the project. If the answer is fifty, starting without one becomes a regret within a year.

Here the answer was five. Three months later it is still five.

SystemsField