From February to October 2026, I worked as a Backend & DevOps Engineer at The Umdenken. My work spanned two products: ULearn, a Ruby on Rails learning management platform, and the company's Django website backend. Across both, I contributed to product features, access control, data processing, deployments, and recovery workflows.
Looking back at that work, the most useful story is how these responsibilities connected. An enrollment feature affects permissions. Video delivery affects the learning experience. An event registration needs consistent data and reliable notifications. Each feature brings questions that extend beyond its API response.
Starting with the workflows behind the product
On ULearn, I worked on the backend workflows that connect learners, organizations, courses, and administrators. These included enrollment, seat allocation, free trials, learning paths, rewards, and referrals. The challenge was keeping the rules consistent across the different ways people used the platform.
A seat could belong to a paid allocation or a free trial. A learner's progress needed to stay consistent across videos, readings, and quizzes. Organization roles affected which actions a person could take. These details shaped the models, validations, authorization checks, and background jobs behind the endpoints.
I also developed marketing workflows: UTM attribution, lead submissions, funnel events, and later proposal engagement features. Connecting a preview or a form submission to a purchase meant treating those interactions as a sequence of business events, with clear rules for when a state should change.
Making access control part of the design
As these workflows expanded, I contributed a migration to permission-based role-based access control. Expressing access through permissions gave the backend a more explicit way to represent what different organizational roles could do.
My authentication work included short-lived access tokens, refresh-token rotation, session-family revocation, and login protections. These changes involved more than issuing a token: they also required handling expiration, logout, and attempts to reuse an old refresh token.
The same access-control questions appeared in media delivery. I extended HLS video workflows using AWS S3 and CloudFront, with signed cookies and URLs for controlled access to course, landing-page, and proposal content. For HLS, access has to cover the relevant playlist and segments, so getting the signing policy and media paths right matters.
A concrete performance improvement
One useful example came from the reward engine. Eligibility checks queried mission logs for individual mission candidates. As the number of candidates increased, the same request could trigger repeated database lookups.
I changed that path to load the relevant logs together, group them by mission, and evaluate eligibility using the loaded data. I also added supporting indexes. In the login flow, I replaced per-session updates with a bulk update when closing active sessions.
The concrete improvement was a change in how the application used the database: batch related work, avoid repeated lookups, and keep the business rules visible.
Those changes are identifiable in the implementation. I do not have a before-and-after latency benchmark to attach to them, so I describe the query improvement rather than claiming a percentage speedup.
Building the Django website and event backend
The Django work covered APIs for articles, classes, media, and contacts, followed by a larger event-management feature. Events brought together registration limits, waitlists, payment-status tracking, exports, and follow-up content such as recordings and recaps.
Registration was a particularly useful consistency problem. Two requests could arrive when only one place remained. The implementation used a transaction and a lock on the event record before checking capacity and creating the registration. It also handled duplicate registrations and transitions such as promoting someone from the waitlist.
Email delivery ran through a database-backed outbox. The registration flow recorded the notification work, while a worker claimed and sent messages outside the HTTP request. Leases and retry backoff supported recovery when a worker or provider failed. This design makes failures manageable, although retries alone do not guarantee exactly-once email delivery.
This part of the project is a good example of how a small-looking feature can involve several engineering concerns at once: concurrency, state transitions, asynchronous work, and operational visibility.
From activity data to retrieval foundations
On ULearn, I worked on asynchronous analytics ingestion using Redis Streams, Sidekiq, and Elasticsearch. The work included event indexing, user lifecycle handling, and privacy-erasure workflows. Keeping an index useful involves maintaining it as the underlying data changes, not just sending events into it.
I also contributed foundations for AI-enabled learning features: transcript processing, chunking, background embedding generation, and permission-scoped vector retrieval with PostgreSQL/pgvector and HNSW indexes.
Authorization remained central here. A relevant passage is only a valid retrieval result if the learner is allowed to access its source. Applying access rules to retrieval connects the AI pipeline back to the same product permissions used elsewhere in the platform.
This work established retrieval foundations. It was not a completed production Q&A assistant: user-facing RAG capabilities still required further integration and operational validation. Keeping that boundary clear is part of describing the engineering work accurately.
Deployment, recovery, and the handover
Alongside feature development, I maintained Docker and GitHub Actions deployment workflows for the Django backend on ECS/Fargate. Toward the end of my time at The Umdenken, my work also included PostgreSQL and Redis backup controls, recovery documentation, backup freshness monitoring, and moving application credentials toward IAM roles.
I introduced Terraform management for selected AWS security and monitoring resources. This was an initial adoption boundary, not a complete infrastructure rebuild. Making that scope explicit matters: versioning some resources does not mean the entire environment can be recreated from the same configuration.
Handover documentation brought these threads together. Deployment procedures, recovery steps, access requirements, and known limitations help the next person understand both how a system works and what still needs attention.
What I take from this chapter
My time at The Umdenken ended in October 2026. The work gave me practical experience across Rails and Django product development, cloud operations, video delivery, analytics, and AI retrieval foundations.
The recurring engineering questions were concrete: Who can access this? What happens if two requests arrive together? What happens when a worker fails? How does the data stay consistent? How will someone deploy, recover, or maintain it later?
Those questions connect the different parts of this experience. They are the habits I want to carry into the systems I build next.