01Why me 02Work 03Experience 04Thesis 05Match me 06Contact

OPEN TO JUNIOR ROLES · RELOCATION OR REMOTE

Berke TayfunAkseki.

$

Software engineer — backend, data & platform. CS + Management graduate from LUISS Guido Carli, Rome (Jul 2026). I build REST APIs in FastAPI, Node.js and ASP.NET Core, queue-driven workers with idempotency and retries, and Airflow/dbt pipelines gated by data-quality tests. Flip on Engineering mode for architecture diagrams and design decisions.

9Public projects
146Regression tests passing
21Regions in thesis panel
4Languages spoken
berke@akseki: ~/portfolio — zsh

Executable value// executable_value

Why hire me?

Four concrete reasons, not adjectives.

Engineer with a management degree

Management and Computer Science at LUISS Rome. The management half shows in how I work: written specifications, recorded decisions, measurable acceptance criteria — and I can read a P&L.

Reliability is the default

Idempotency keys, FOR UPDATE SKIP LOCKED queues, retries behind circuit breakers, a seeded deterministic rules engine with 146 regression tests passing. I build for the failure case first.

Data depth across three stacks

Airflow, PySpark and dbt for pipelines; R for econometric panel analysis; raw SQL with window functions and CTEs — plus KNIME and Power BI from a client-facing P&G team project.

Clear work authorisation

Turkish citizen — no permit needed in Turkey. Italy: entitled to a 12-month post-graduation residence permit, convertible to a work permit with no employer quota wait. Open to relocation and remote, available now.

Proof of work// projects --all

Selected work

Problem → solution → result. Every public repo is linked. Switch to Engineering mode for architecture, trade-offs and what broke along the way.

~/projects/energy-connect-platformpublicSource ↗

Backend · Distributed systems · Cloud

Energy Connect Platform

TypeScript/Node.js connectivity layer for EV chargers and vehicles — my largest system.

ArchitectureSeparate API and worker processes over a durable PostgreSQL command queue
ReliabilityIdempotency keys, exponential-backoff retry, circuit breaker, dead-letter state
CloudAWS CDK → CloudFormation: ECS/Fargate, RDS, CloudWatch alarms, autoscaling

Problem. An energy platform sits in front of OEM APIs that time out, rate-limit and fail in ways the product above them must never notice. One flaky vendor cannot be allowed to degrade the whole platform — and a retried request must never start a car charging twice.

Solution. A connectivity layer built to isolate failure. Commands are written to a durable PostgreSQL queue with an idempotency key; workers claim rows using FOR UPDATE SKIP LOCKED so two workers never pick up the same command; every provider call is wrapped in retry plus circuit-breaker logic. Structured logs, Prometheus metrics and an SLO snapshot make failure visible, and a React operations dashboard shows device state, queue depth and SLO health. Backend tests run in GitHub Actions against a live PostgreSQL service container.

[React ops dashboard]      [client / integrator]
             \                    /
              v                  v
        [Node.js API — TypeScript]
                    | enqueue command + idempotency-key
                    v
        [PostgreSQL — devices + command_queue]
                    ^ FOR UPDATE SKIP LOCKED
                    |
           [Node.js worker pool]
                    | retry (exp. backoff) + circuit breaker
                    v
        [Simulated OEM A]   [Simulated OEM B]

 observability: structured JSON logs · Prometheus metrics · SLO snapshot
 infra: AWS CDK > CloudFormation > ECS/Fargate · RDS · CloudWatch alarms

// engineering decisions

  • Why a PostgreSQL queue instead of SQS or Kafka? Command state and queue state have to stay consistent with each other. Keeping both in one transactional database removes a whole class of "the message was sent but the row was never written" bugs — and at this scale a broker is infrastructure to justify, not use.
  • Why FOR UPDATE SKIP LOCKED? It lets several workers drain one table concurrently: each worker locks the rows it claims and skips whatever is already locked, so throughput scales with worker count instead of serialising behind one lock.
  • Why a circuit breaker on top of retries? Retrying against a provider that is already down turns one outage into a self-inflicted load test. The breaker trips after a failure threshold and fails fast until the provider recovers.
  • Why idempotency keys at the API edge? Clients retry. Without a uniqueness constraint on the key, a network hiccup silently becomes two START_CHARGING commands — invisible in tests, expensive in production.
  • Why AWS CDK rather than raw CloudFormation? The stack is typed TypeScript, so infrastructure is checked by the same compiler as the application. cdk synth still emits plain CloudFormation.

// lessons learned

  • Reliability code is only real if it is tested against failure. Retry and circuit-breaker units are tested with deliberately failing providers — proving the breaker actually opens is the whole point.
  • Splitting API and worker changed the design. Once the processes cannot share memory, every piece of coordination has to become explicit state in the database — which is what made the queue design necessary.
  • Next: real OEM adapters behind the existing provider interface, Grafana dashboards checked into the repo, blue/green deploys on ECS.
TypeScriptNode.jsPostgreSQLReactDockerAWS CDKPrometheusGrafana
~/projects/parcain progressPrivate · walkthrough on request

Aug 2026 → present · Backend · Deterministic systems

Project PARCA

Deterministic rules engine and game server for a multi-day, eight-player strategy game.

DeterminismSeeded RNG replays an entire season exactly — any result is reproducible
Testing146-case regression suite · currently 146 passed, 0 failed
ServerASP.NET Core · server-authoritative UTC clock · transfer ledger

Problem. A strategy game played over several days by eight people lives or dies on trust: if a result is disputed, it has to be provable — and no client can be allowed to touch a balance, a deadline or an outcome.

Solution. A deterministic rules engine in C#: a seeded RNG means an entire season can be replayed exactly, so any disputed result is reproducible and auditable. The ASP.NET Core server owns time (a server-authoritative UTC clock) and money (a transfer ledger), so no client can alter a balance, a deadline or a result. A 146-case regression suite covers eight negotiation minigames, transfer rules, elimination boundaries and season-end conditions.

// engineering decisions

  • Why a seeded RNG? Randomness that can be replayed turns "the game cheated me" into a question with an answer: re-run the season from the seed and compare.
  • Why server-authoritative time and money? Deadlines and balances are the two things a client would most want to change. Keeping both on the server — clock in UTC, every transfer in a ledger — removes that attack surface entirely.
  • Why a regression suite this early? Rules interact. Minigames, transfers and elimination boundaries all touch the same state, so every rule change re-runs all 146 cases.

// product side

  • I own the product as well as the code: design document, economy specification and a log of 14 recorded decisions, each tied to a testable hypothesis.
  • Pace: 174 commits in the first three weeks.
C#.NETASP.NET CoreJavaScriptBlender
~/projects/analytics-data-platformpublicSource ↗

Data engineering · Orchestration

Analytics Data Platform

End-to-end batch pipeline: Airflow → PySpark → Parquet → PostgreSQL → dbt.

OrchestrationAirflow DAG drives ingest, transform, load and dbt build in order
TransformPySpark schema validation, typing and deduplication into Parquet
Modellingdbt staging + daily-sales mart, gated by data-quality tests

Problem. "I know SQL" and "I can build a data platform" are different claims. The second needs orchestration, a transformation engine, a warehouse, a modelling layer and tests that fail loudly when the data is wrong.

Solution. A reference pipeline that runs the whole loop: raw order events are validated, typed and deduplicated in PySpark, written as Parquet, loaded into a PostgreSQL warehouse, then modelled in dbt as a typed staging layer and a daily-sales fact table. The marts sit behind dbt data-quality tests, so a bad load fails the Airflow DAG instead of silently reaching a dashboard. Docker Compose reproduces everything locally with one command.

[data/raw/orders.csv]
        | ingest
        v
[PySpark — schema validation · typing · dedup]
        | write
        v
[Parquet — partition-friendly columnar]
        | load
        v
[PostgreSQL warehouse]
        |
        +-- dbt staging: stg_orders  (typed, cleaned)
        |
        +-- dbt mart:    fct_daily_sales  + schema.yml tests

 orchestration: Airflow DAG · env: Docker Compose · CI: GitHub Actions

// engineering decisions

  • Why Parquet between Spark and Postgres? Columnar output is cheap to re-read and re-load, so a failed load can be retried without re-running the Spark job.
  • Why dbt on top of SQL I could write by hand? Dependency-ordered builds, a documented schema, and tests that run as part of the pipeline rather than as a separate good intention.
  • Why deduplicate in Spark, not the warehouse? Deduplicating before the load keeps staging a faithful record of what arrived, not a place where cleaning silently happens.

// lessons learned

  • Orchestration is where the real design lives. Each step is easy alone; making them idempotent and re-runnable in a fixed order is what takes thought.
  • Next: incremental dbt models, partition-aware Spark writes and source freshness checks.
Apache AirflowPySparkdbtPostgreSQLParquetDockerGitHub Actions

More projects

open any card for the full case study

ML pipeline · Backendpublic

Jobscope

Swiss/EU job-market tracker — aggregates 200+ live listings from two APIs and ranks each 0–100 against my profile with TF-IDF cosine similarity.

FastAPIscikit-learnSQLiteJWTDocker
Case study

Problem. Tracking junior roles across Swiss and EU boards by hand meant duplicates, stale posts and no way to rank fit.

Solution. Fetch from RemoteOK and Adzuna, normalise URLs to kill duplicates, score every posting against a candidate profile with TF-IDF + cosine similarity, and serve it all through a JWT-authenticated FastAPI backend in Docker.

[RemoteOK API]   [Adzuna API]
       \             /
   [Ingestion — requests]
              |
   [Dedup — normalised URL hash]
              |
      [TF-IDF matrix] <──> [candidate profile]
              |   cosine similarity → 0–100
        [SQLite] <── [FastAPI + JWT] <── client

// decisions

  • Why SQLite? Read-heavy with batch writes — a Postgres container adds ops cost for zero benefit at this scale.
  • Why TF-IDF over LLM embeddings? Job ads are keyword-dense; sparse vectors rank well at zero compute cost and no API dependency.
  • Lesson: the same posting appears on several boards under different URLs — normalising before hashing fixed silent double-counting.
Statistics · Experimentationpublic

Causal Lab

A/B testing and difference-in-differences toolkit — power analysis, two-proportion z-tests, sample-ratio-mismatch detection and a two-way fixed-effects DiD estimator.

SciPystatsmodelsFastAPIStreamlit
Case study

Problem. Most portfolio ML is prediction. The experimentation side — deciding whether a launch actually caused a metric to move — is usually skipped.

Solution. Power analysis to size a test, a two-proportion z-test with proper intervals, an SRM check that flags broken randomisation before anyone trusts a p-value, and a DiD estimator for when you can't randomise at all — the estimator family from my thesis.

// decisions

  • Clustered standard errors in DiD. Without clustering at the unit level, SEs are understated whenever outcomes correlate within a unit over time.
  • Validated against injected ground truth. Tests prove the statistics recover a known effect — not just that the code runs.
  • Lesson: simulating "no true effect" and checking the false-positive rate caught a real alpha-violation bug.
Applied AI · DevOpspublic

Regulatory RAG Assistant

Retrieval-augmented Q&A over regulatory PDFs — chunking, embeddings, FAISS semantic search, served via FastAPI and Streamlit with CI on every push.

FAISSFastAPIStreamlitDockerGitHub Actions
Case study

Problem. Regulatory documents are long and dense; keyword search misses context entirely.

Solution. Ingest PDFs, chunk with overlap, embed into a FAISS index, retrieve the most relevant passages per query, and serve results over REST plus an interactive UI.

// decisions

  • Why FAISS? In-memory index: millisecond retrieval, no vector-DB service to run or pay for.
  • Why decouple UI from API? Streamlit is for demos; the REST layer lets any client consume retrieval.
  • Lesson: chunking strategy mattered more than the model — overlap preserved clause context.
SQL · Product analyticspublic

SQL Analytics Lab

Cohort retention and funnel analysis in raw SQL — LAG, FIRST_VALUE, running totals, RANK vs DENSE_RANK — against a synthetic app-usage dataset.

SQLWindow functionsCTEsSQLite
Case study

Problem. "Compute N-week retention by cohort" and "rank X by Y, handling ties" are easy to get subtly wrong.

Solution. Analytical SQL with no ORM and no pandas standing in for what SQL should do: funnel conversion, weekly cohort retention, rolling averages and country rankings that show the RANK/DENSE_RANK difference on ties.

// decisions

  • LAG() over a self-join for step-over-step drop-off — idiomatic, and every modern engine optimises it.
  • Lesson: my first comment-stripping logic silently dropped every query, because each statement opened with a comment header. Stripping comment lines before splitting fixed it.
Client project · Teampublic

Oral-B Geo-Analytics 2026

Store-level campaign optimisation for a P&G geo-targeted Oral-B campaign in Italy — predict which stores will respond and recommend the 2026 activation list.

KNIMEPower BIRandom ForestLogistic Regression
Case study

Problem. The 2025 campaign had mixed results: some stores generated incremental sales, others none — budget was being spent where it didn't work.

Solution. Feature engineering on store data, logistic regression and random forest models to score each store's probability of success, a ranked activation list with an expected-profit "size of prize", and a Power BI dashboard with map-based exploration.

My part. Built the analytics workflow and trained the models in KNIME, supported evaluation (ROC/AUC, confusion matrix) and scoring, contributed to the final delivery.

ML · Applied statspublic

Churn Prediction API

Customer-churn classifier with a reproducible scikit-learn pipeline, served through a FastAPI endpoint and explored in a Streamlit dashboard.

scikit-learnFastAPIStreamlitjoblib
Case study

Problem. A model in a notebook isn't useful to a business stakeholder — it needs to be queryable and visual.

Solution. A preprocessing + training Pipeline evaluated on accuracy, ROC-AUC and a confusion matrix, wrapped behind a prediction API, with a dashboard so a non-technical reviewer can explore predictions directly.

Game dev · Networkingprivate

Labyscape

2–8 player co-op horror game in Unity 6 with Netcode for GameObjects — procedural depth-first-search mazes and host-authoritative multiplayer.

Unity 6C#NetcodeNavMesh
Case study

Problem. Real-time multiplayer needs every player to see the same maze, the same enemies and the same damage events.

Solution. The host generates an 18×18 DFS maze at session start and replicates it; NavMesh enemy AI, health and respawn state synchronise through NetworkObject state.

// decisions

  • Why DFS? It guarantees a fully connected maze — no unreachable rooms, ever.
  • Lesson: replicate what changes, not what exists — sending the maze seed instead of the grid cut join-time payload.
Full-stack · Authpublic

Django CRUD + Auth

Server-rendered book catalogue with registration, login, guarded CRUD routes and image uploads — built by hand rather than leaning on the admin panel.

DjangoSQLiteHTML/CSS
Case study

Problem. Prove an understanding of the request/response cycle without the framework's admin doing the work.

Solution. Hand-written models, views and templates; authentication guards on every write route; image upload handling.

Track record// git log --oneline

Experience & education

AUG 2026 → PRESENT

Project PARCA · independent · C# / ASP.NET Core

  • Deterministic rules engine and game server for an eight-player strategy game — 146-case regression suite, 146 passing.
  • Own the product side too: design document, economy spec, 14 recorded decisions tied to testable hypotheses.

FEB 2026 → MAY 2026

Creative Manager Intern · LUISS Language Cafe, Rome

  • Owned creative direction and end-to-end delivery of the programme's communications across the university's official channels.
  • Coordinated stakeholders across university departments on message, format and timing, to fixed publication deadlines.

SEP 2023 → JUL 2026

BSc Management and Computer Science · LUISS Guido Carli, Rome

  • Joint degree: algorithms, databases and big data, AI and machine learning, statistics and quantitative models, plus the management and economics core.
  • Thesis: macroeconomic resilience of 21 Italian regions — a 2015–2024 panel in R with difference-in-differences and an event study. See below ↓

Empirical rigour// thesis.R

Bachelor's thesis

Econometric research on real EU data — the analytical foundation behind the engineering.

"Tourism Dependence and Regional Macroeconomic Resilience in Italy: The Economic Effects of the COVID-19 Shock"

Difference-in-differencesEvent studyPanel dataREurostat / ISTAT

Panel analysis of 21 Italian NUTS 2 regions over 2015–2024 using a continuous DiD framework with an event-study specification. Identified structural labour-market hysteresis as the mechanism behind persistent post-pandemic divergence in high-tourism regions. Supervisor: Prof. Diletta Topazio, LUISS Guido Carli.

Yit = α + β(Postt × Treatmenti) + γXit + δi + λt + εit

β estimates the differential post-shock effect on tourism-dependent regions, with region (δ) and time (λ) fixed effects.

event_study.R — employment index, 21 NUTS-2 regions

tourism-dependentcontrol
80859095100105 COVID shock 2015201620172018201920202021202220232024

# Parallel pre-trends → sharp 2020 divergence → asymmetric recovery.
# Tourism-dependent regions never return to the counterfactual = structural hysteresis. Illustrative, reconstructed from the thesis findings.

21NUTS 2 regions over a 10-year panel (2015–2024)
2Original propositions: tourism recovery asymmetry & structural hysteresis
30Grade in Statistics — the top score in the Italian system
RFull data preparation and estimation pipeline built in R

REST paradigm// GET /api/resume

GET /api/resume

The CV, as a backend engineer would serve it.

GET berketayfunakseki.com/api/resume200 OK
{
  "candidate": "Berke Tayfun Akseki",
  "title": "Software Engineer — Backend, Data and Platform",
  "education": {
    "institution": "LUISS Guido Carli University, Rome",
    "degree": "BSc Management and Computer Science (L-18)",
    "graduated": "2026-07"
  },
  "stack": {
    "languages": ["Python", "TypeScript", "JavaScript", "C#", "SQL", "R"],
    "backend": ["FastAPI", "Node.js", "ASP.NET Core", "Django", "SQLAlchemy", "Pydantic"],
    "data": ["PostgreSQL", "Airflow", "PySpark", "dbt", "Parquet", "Pandas"],
    "ml_stats": ["scikit-learn", "statsmodels", "SciPy", "FAISS", "Power BI"],
    "cloud_devops": ["Docker", "AWS (ECS/Fargate, RDS, CDK)", "GitHub Actions", "Prometheus", "Grafana"]
  },
  "experience": [
    { "role": "Project PARCA — rules engine & game server", "since": "2026-08" },
    { "role": "Creative Manager Intern, LUISS Language Cafe", "from": "2026-02", "to": "2026-05" }
  ],
  "languages_spoken": { "tr": "native", "en": "C1", "fr": "B2", "it": "B1" },
  "work_authorisation": {
    "TR": "citizen — no permit required",
    "IT": "12-month post-graduation permit, convertible to work permit"
  },
  "location": "İzmir, Turkey — open to relocation and remote",
  "public_projects": 9,
  "available": true
}

Try it on me// match(jd, me)

Paste your job description.
See how I match.

Keyword matching in the spirit of my Jobscope project: it checks the job description against my real stack and shows what lines up and what doesn't. It runs entirely in your browser — nothing you paste leaves this page.

Score = share of recognised tech mentions in the JD that I've shipped with. Ranking real listings with TF-IDF lives in jobscope.

Get in touch// open_socket()

Let's build
something.

Looking for junior backend, data or platform roles — relocation or remote. If you're hiring, I'd love to talk.

Available now İzmir local time Based in İzmir · open to relocation & remote
✓ Offer extendedyou ran 'sudo hire-me' — let's make it official → berketayfunakseki@gmail.com