Tackle problems without ready-made solutions
I take work from the first unclear requirements to a working result. I dig into unfamiliar systems, investigate constraints and find a way forward.
Open to senior roles
C++ · Backend · AI-assisted DevelopmentBatumi, Georgia
I solve complex problems and see projects through to completion.
01 / What I do
I take work from the first unclear requirements to a working result. I dig into unfamiliar systems, investigate constraints and find a way forward.
From data models and component boundaries to how the whole system works together. I account for product constraints and plan how to carry a solution through implementation and rollout.
I organize development with agents: defining tasks, preparing context and environments, working through solutions, reviewing changes and verifying results. I build feedback loops through tests and measurements.
I plan the stages, coordinate dependencies across teams, and help work through the difficult parts to get the release out.
I investigate latency, memory use and performance. I find causes, test hypotheses through experiments and make changes at the level that delivers the needed result.
I make builds, checks and deployments reproducible. I shorten feedback loops, automate repetitive work and create a smooth process for delivering changes.
I introduce changes to complex, long-lived codebases with care for stability and quality.
I dig into the problem and the meaning of the data, revisit constraints and identify complexity that can be removed.
02 / Highlights
The task was to distribute market data to multiple recipients with low latency. I split the work into several stages.
I am proud that in just a few days I built a solution in a completely new field for me that held its own against experienced competitors from the HFT industry.
Separate city datasets were a limitation built into 2GIS from its beginnings. Users could only work within one city, even when two cities were close together, like Samara and Tolyatti. They could not plan a route from one to the other, search across the boundary or see the results on a shared map. Solving this required rethinking the architecture of the entire application and the data flows feeding it. Almost every core module needed changes, from the update system to the map, directory and beyond.
I was responsible for delivering this project across the whole department, not just my team. I broke down the work, identified cross-team dependencies, created and maintained the roadmap, ran regular coordination meetings with adjacent teams, designed the architecture and personally implemented several complex components.
The challenge was both to carry such a large transformation through more than two years of development to release and to do it inside a complex live product, with regular releases and a steady stream of other features often built without accounting for the new model.
It felt damn good to hear the head of mobile say that we pulled this off in large part thanks to me.
The second legacy of 2GIS’s origins as a PC product distributed on CDs was its offline-first model. In the 2020s, asking someone to download hundreds of megabytes after installation before they could start using the app was too high a barrier. Users left at the database-download screen, which also made a major advertising investment wasteful: much of the acquisition budget would be lost before people even tried the product.
Online mode had to be ready for an advertising campaign scheduled exactly a year ahead. This meant a major transformation of a live application: the map, directory, search and data-loading model had to support several modes at once, while regular releases and parallel feature development continued. I led the entire feature, working through difficult trade-offs between architectural constraints, product requirements, performance and deadlines.
Midway through development, I realized that the chosen approach to backend interaction was falling short of expectations. I designed a new one that combined fast responses with data consistency, which was critical to both the product and the core architecture.
A few months before the deadline, it became clear that we could not finish with the engineers we had. I escalated the need for additional engineers from adjacent teams and, once again, worked with the product manager to revise the scope of must-have features for the first release. Replanning, daily coordination, clear checkpoints and systematic stabilization helped us deliver the project.
We launched online mode on time, reducing the churn rate immediately after installation from 22.3% to 7.3%.
The main challenge was meeting a hard one-year deadline, with no room for a missed release. Many later acknowledged that a couple of years would have been a more realistic timeframe for changes of this scale.
The new Zenith 3D engine had been in development for about seven years and was designed to bring fundamental changes to the product: a seamless map, online mode and flexible styling. It also introduced a completely new data storage format, incompatible with its predecessor, and a different API. These differences made integration exceptionally difficult, with no way to sidestep the underlying incompatibilities. I took on the task and worked through it step by step.
I started by building a prototype to explore the entire path: from calls to the engine API, through new core interfaces, to rendering frames in a Qt/QML application.
I then designed runtime and compile-time flags to isolate the new integration. This let us carry out a long development effort on the main branch without affecting production code before release.
Next, each component that used the engine needed either a parallel implementation against the new API, making full use of its capabilities, or a temporary adapter to speed up integration.
Migration was a separate challenge. We could not let users with old-format data suddenly lose the ability to use the app. I proposed selecting the engine at runtime based on the available data format and recreating the entire core session when switching between incompatible datasets. This let users move smoothly to the new version without disruption.
The hardest part was meeting the strict requirement for backward compatibility with existing data when the engine itself had not been designed to support it. I had to devise a mechanism outside the engine to detect the data format and switch engines, and teach all of the core code to work with both.
The existing delivery mechanism had a long list of problems: memory spikes when loading and updating data, a large idle memory footprint, slow queries, and unreliable data storage. Fixing each problem separately would add up to a major effort, with no guarantee of success.
I proposed an alternative: redesign the entire system for a comparable amount of work. At its core, I put our compact proprietary read-only key-value database, which offered high compression and fast queries. To support an EAV data model with complex filtering, I added hash indexes — one for each distinct filter used in the product.
To support frequent updates without downloading the entire database, I designed a system of patch databases. The difference between two data snapshots becomes a small, separate database in the same format. For a given base database, only the patch needs to be downloaded again. The client merges query results on the fly to produce the target data view.
Before committing to a refactor of this scale, I used inexpensive prototypes to prove that the design met the performance and data-size targets. That resolved the core uncertainty before we invested in implementation.
The task was outside my area of responsibility, but I saw a way to make better use of the allocated resources. I proposed an alternative and proved its potential up front. The design combined a range of architectural techniques to deliver high performance and reliability while keeping the resulting system simple and straightforward.
CI in the 2GIS core team started as a minimal set of Jenkins jobs configured by developers to check the master branch after merges. Under my leadership, it became a dedicated area of work with its own goals, priorities and roadmap, and the infrastructure developed into a mature solution.
I am proud that the tools built by me and the engineer I managed enabled fast, reliable and convenient code delivery for developers every day.
Our TypeScript library was the main way users interacted with the cluster, so we decided to move the CLI from Go onto that library too. This would eliminate the need to maintain two implementations that occasionally behaved differently.
Go had set a high bar for the CLI executable’s portability, so we wanted the new version to ship as a single statically linked binary too.
That turned out to be difficult. Our build system used Nix, which could theoretically build packages from its catalog statically. In practice, it was far from straightforward. I worked through numerous build failures, adding patches and configuration flags along the way. Some problems were specific to macOS, including codesign inside Nix’s hermetic build environment.
The next hurdles appeared when packaging our oclif-based CLI as a SEA (Single Executable Application — Node.js’s experimental support for single-file distribution). I effectively had to implement a small virtual filesystem by patching require.
The statically built Node.js runtime could not load libraries dynamically, so I also had to replace some native dependencies with WASM implementations.
I also worked on startup performance to make the switch to the new version smooth for users.
This task meant getting to grips with an unfamiliar technology stack and building a solution on top of it, while overcoming a staggering number of obstacles along the way. That made it all the more rewarding to put my strengths to work and see it through to completion.
03 / Experience
Backend Developer
Batumi, Georgia
Developed server components, developer tools and infrastructure for the nil blockchain.
Took tasks from architecture and prototypes through integration, deployment and verification on a real cluster.
Investigated critical failures and coordinated fixes, helping the team move from a monolithic prototype to a distributed deployment.
Senior C++ Developer
Batumi, Georgia
Worked on architecture, data, memory and performance in the 2GIS C++ core. Took on open-ended engineering problems. Built prototypes, tested architectural hypotheses using real data, coordinated with other teams and took integrations through release.
Mobile Application Core Team Lead
Novosibirsk, Russia
Led delivery of major features, owning architecture, planning and the team’s results. Combined implementation and technical guidance with cross-team coordination. Hired and developed engineers, including mentoring and helping them take ownership of individual areas.
C++ Developer
Novosibirsk, Russia
Owned development of major features spanning multiple teams. Designed architecture, established cross-team collaboration, broke down the work, built and maintained roadmaps, and implemented early prototypes to validate ideas.
CI Engineer
Novosibirsk, Russia
Built CI for a large cross-platform C++ project: made infrastructure reproducible, shortened the full build cycle and introduced required checks before merging. Set the direction, hired engineers and gradually handed over ownership of CI.
C++ Developer
Novosibirsk, Russia
Established delivery of the new Qt/WebUI desktop application for Ubuntu and macOS.
Investigated a possible migration to QtWebEngine by studying the Qt and Chromium source code.
Junior C++ Developer
Novosibirsk, Russia
Maintained and extended the 2GIS desktop application, the company’s main revenue-generating product.
Perl Developer
Novosibirsk, Russia
Automated internal processes in the Data Production Department and developed web crawlers to collect organization data from public sources.
Frontend Developer
Novosibirsk, Russia
Developed web projects and led delivery of several new projects with fixed deadlines.
Improved the frontend development workflow by introducing BEM and SCSS.
Roles, tasks and technologies.
04 / Tasks
Investigated the map’s dependency graph and removed synchronous waits for several heavyweight services, while also warming up several others in advance. This made subsystem initialization up to twice as fast and allowed styles to be prepared 200–300 ms earlier.
Also found that the 3D engine repeatedly prepared the same data. Built a prototype that cached the results, making the map appear 20% faster on low-end devices. Handed the work over to the engine team, who took it through to release.
Helped build the mobile SDK team from scratch by organizing the hiring process. Reviewed CVs, sent outreach emails and conducted interviews. Wrote job postings and tailored messages to each candidate’s experience. Within a tight timeframe, we hired more than 10 strong C++ developers across several teams.
Wrote an automated crawler that expanded the candidate pool while reducing the workload for HR specialists.
Handed the hiring process over to colleagues: shared materials, worked through introductory meetings and technical interviews together, and gradually delegated the early stages.
The 2GIS mobile app mapped all data containers into memory at startup. This guaranteed high performance but consumed substantial virtual address space, which was often severely limited on mobile devices—iOS and 32-bit Android in particular. The requirement to update data on the fly threatened to make matters worse: at that point, we would face a peak twice as high.
Took a systematic approach, starting by making the problem measurable. Built a Google Benchmark harness and a gperftools-based memory manager that counted both ordinary allocations and file mappings. Once the baseline was explicit and reproducible, moved on to the optimization itself.
Reworked the VFS to map individual regions on demand instead of eagerly mapping an entire container. Data access almost always touched only a small subset of the files inside, so this radically reduced the footprint. The benchmark confirmed the improvement.
As we developed the nil blockchain, we gradually moved from a monolithic application to a distributed cluster. I was responsible for separating the RPC node from the validator.
To keep development and debugging simple, the application still needed to run in a single process. I abstracted the transport layer so the RPC component did not need to know whether it was calling the validator locally or over the network. We used libp2p for network transport; I built a Protobuf-based protocol on top. I made extensive use of Go generics and reflection to minimize boilerplate. The result was concise, expressive code.
Different node types and configurations needed to serve different sets of protocols: read-only, read-write and debugging calls. I refactored registration and split the interfaces into non-overlapping hierarchies, allowing them to be combined as needed without extra branching.
Alongside the code, I set up deployment of the new configuration in the test and production environments, learning how the infrastructure worked along the way.
Initiated and drove the move to C++20 across all C++ development at the company. Made the necessary changes to accommodate limitations in Clang, GCC, MSVC and project dependencies. Coordinated changes between teams and promptly fixed issues as they emerged. Once the standard was enabled, actively promoted its features through my own code and reviews: concepts, the spaceship operator, consteval, template lambdas and more.
Introduced clang-format and initiated the formalization of unwritten code style conventions.
Investigated and fixed compilation-time problems. Used -ftime-trace and Templight++ to analyze heavy include dependencies and expensive template instantiations. Tuned the contents of the precompiled headers.
Promoted compile-time guarantees through the type system. In particular, implemented a library of non-null identifiers and zero-cost optional wrappers around them.
Introduced tools to make code more expressive and simpler, including a std::source_location polyfill and magic_enum.
Initially, nil’s macOS builds in GitHub CI followed a separate path that bypassed Nix, the project’s standard build system. This blocked the release of the new CLI, which had a nontrivial Nix build process. I set out to fix this and unify the Linux and macOS builds.
The Nix cache was a crucial part of the puzzle. Without it, every CLI build compiled Node.js from scratch, taking more than an hour and burning through runner limits. On the autoscaling Linux runners, the cache was configured when each node was provisioned. For the standard macOS runners, that setup had to become part of the build workflow.
Successful builds needed permission to write new artifacts to an AWS S3 bucket. Instead of putting a long-lived token in GitHub secrets, I followed the recommended approach: obtaining short-lived credentials through GitHub CI’s OIDC integration with AWS. I added an OIDC provider in Terraform and configured the build workflow to assume the corresponding role.
This resolved the inconsistency between platform builds and enabled the pipeline to produce the required artifacts.
The mobile SDK project was developing new versions of components already used in the product: building floor plans, traffic, route visualization and more. Our team needed them for online mode, but I could also see a navigator redesign underway that would introduce a minimap. I knew the shared solution had to account for both projects. I brought the full picture together and designed changes that served both efforts without pulling them in conflicting directions.
The integration had to reconcile different object lifecycles in the core and SDK, avoid reference cycles and dangling references, and prevent initialization races. This called for carefully balanced transitional solutions that worked with the internals of both systems. The existing architecture also assumed that the main map was always present, and many components expected access to it during startup. Additional maps had to fit within those constraints. I designed a mechanism to manage them, agreed it with the platform teams, implemented the changes and built a minimap prototype in the C++ core and the Qt/QML-based Android app.
The minimap used SDK components to render the route line and progressively trim the travelled portion, display and track the location marker, and apply the new styles. It also had an important advantage for this kind of integration: it was a new component, so we could use SDK capabilities without replacing a complex existing implementation. SDK-based route rendering therefore reached the minimap long before the main navigator. We had found an isolated part of the product where the new code was already delivering value, and further development had to preserve that compatibility.
The transitional integration was fragile in places, but using it in the main branch changed who was responsible for compatibility: subsequent SDK changes now had to account for the working app. We no longer had to keep catching up on a separate branch.
05 / Projects
Built a vision-board service from an idea into a working product with real users and payments.
Owned the project end to end: made product decisions, designed the architecture, developed, tested and operated the service on Google Cloud.
06 / Stack
The stack doesn’t matter — getting the job done does.
I deliver
07 / Writing
Conference talks, an architecture interview and a technical deep dive. Talks are in Russian.
08 / Education
Applied Mathematics and Computer Science
Novosibirsk, Russia
09 / Contact
I’m looking for a team with a great product, ambitious goals and interesting engineering challenges.
Open to senior roles
Get in touch