Today, we’re sitting down with Georgii Perepechko, a Staff Software Engineer at Signal Ocean. With a diverse background spanning from tech giants like Meta and Yandex to educational roles and freelance work, Georgii has made significant contributions across various aspects of software development. Let’s dive into his experiences and insights in the world of frontend engineering and beyond.
Your career has taken you through companies like Signal, Meta, and Yandex. How have these experiences shaped your approach to frontend development?
I’d say my diverse experiences have made me a well-rounded engineer, and I genuinely value what I learned at each place. Starting out, I was focused purely on HTML and CSS before moving into JavaScript and taking on more complex tasks at an Ed-Tech startup, like building 2D games in Phaser and creating a UI for a chess engine. This extremely fast-paced environment with a week-long development cycle taught me to prototype quickly and pick up new skills on the fly — qualities that are still invaluable to me today.
At Yandex, the learning curve steepened. I joined as an intern and realized I had to rethink how I approached development. It wasn’t just about coding; it was about having a deeper understanding of design patterns, thinking critically about optimizations, and focusing on clean, maintainable code that serves both developers and users. Yandex pushed me to become not only a frontend specialist but a software engineer with a solid grasp of architecture, CI/CD, and best practices. This broader knowledge base gave me the confidence to take on larger projects and become less tied to a specific stack.
Joining Meta brought an entirely new perspective. Many technical tasks were abstracted by tools and frameworks built by brilliant engineers, so my role evolved to focus more on product ownership. I was not just designing project architecture and coding but also running experiments, analyzing UX tests’ results, coordinating with other teams, and working with security and privacy auditors. This experience made me realize that, while code quality is essential, it must ultimately serve the user’s needs and safety — a huge shift in perspective.
Finally, at Signal, I found the balance between technical challenge and personal impact. I work on complex features but also get to question and refine my approach based on our users’ needs. Signal has taught me not just to accumulate knowledge but to apply it thoughtfully, identifying areas for improvement within our app, processes, and even messaging.
If I had to sum it up, the biggest lesson from all these experiences is to stay curious and proactive. Knowing the “why” behind each feature and understanding the entire app lifecycle allows you to constantly evolve, finding ways to improve both UX and internal processes.
You’ve built multiple complex web and mobile apps from the ground up. Can you walk us through one of your most challenging projects and how you tackled it?
One of the most challenging projects I worked on was building a complex multi-tenant dashboard for tracking real-time support and sales phone calls across several call centers. The app needed to handle live calls and updates for thousands of users across different tenants, each with unique configurations, while remaining performant and highly interactive.
On the backend, the biggest challenge was designing for customization. Each organization—and even groups within them—had its own call settings, client routing rules, operator assignment logic, unavailability fallbacks, and voice prompts. This required orchestrating interactions between multiple microservices written in different languages, integrating several frontend apps, and ensuring robust deployment workflows. Fortunately, we could leverage a significant portion of previously written WebRTC call logic from an earlier project, adapting it with minor tweaks to meet the new requirements.
On the frontend, managing real-time data updates without overwhelming the browser or compromising the user experience was a major hurdle. To address this, we implemented WebSockets for live updates and optimized state management with reactive libraries. Virtualization techniques were employed to render large datasets, such as thousands of table rows, ensuring smooth performance and responsiveness. Adding to the complexity, one of the apps we developed was an iframe widget, which introduced additional challenges in client-side communication and cross-origin interactions.
Beyond the technical challenges, the project emphasized the importance of balancing performance, flexibility, and user experience. It required close collaboration with the product team to prioritize features, identify potential bottlenecks early, and iterate continuously based on feedback.
Overall, the experience was a valuable reminder of the importance of thoughtful architecture, strategic code reuse, and adaptability in solving complex problems—especially in systems that demand both high performance and extensive customization.
At Meta, you worked on account recovery frameworks. What were the key considerations in designing a scalable and secure system for this critical function?
As front-end developers, we’re often somewhat shielded from security concerns by frameworks and our backend counterparts. But working on a security-focused project taught me to approach every decision with a security-first mindset. In sensitive areas like account recovery, the emphasis must be on prevention; a fast or overly convenient recovery flow can create vulnerabilities. This made it essential to set clear user expectations—account recovery, especially for suspicious accounts, won’t always be instant or smooth, but this inconvenience is a necessary trade-off for long-term safety.
User education became a critical part of this work. We focused on promoting multi-factor authentication (MFA) and provided guidance on best practices for account protection. Additionally, we implemented warnings when user actions seemed risky or exposed sensitive data. An informed user is often the best defence, so clear, actionable feedback was a priority.
We also took a conservative approach to data exposure—limiting data fetching, implementing permission levels rigorously, and using whitelists over blacklists where possible. Assuming user-provided data is benign is a mistake; instead, every piece of data should be treated with caution.
All in all, security is an adversarial, constantly evolving field, with new tactics and social engineering methods emerging all the time. So, our goal was to create a secure, user-friendly experience that would work seamlessly for most users while balancing stricter measures for higher-risk scenarios. The biggest takeaway was learning to strike that delicate balance between usability and security, ensuring strong protections without making the process overly complex for legitimate users.
You’ve had experience with both web and mobile development, including a complete rewrite of native apps to Flutter. How do you compare the development process for web versus mobile, and what led to the decision to use Flutter?
Web and mobile development differ in several significant ways.
As a web dev, you generally assume users are always on the latest version of your application — they get updates every time they load a page, or at worst, may need to refresh. With mobile apps you have to be ready for a wide range of versions still in use. Backwards compatibility becomes the thing to watch out for. Any update could break functionality for users on older versions, and app store review times only add more to the problem.
Device variety also brings challenges. Even though web applications have to support a range of devices, browsers often abstract away many of these issues. We don’t need separate websites for iOS and Android, nor do we typically worry about prehistoric devices without touch screens or new foldables that change display behaviour. But in mobile, these variations become critical, and they directly impact the UX.
On the other hand, mobile apps run closer to the hardware, which can give them better performance and access to more APIs. Languages like Kotlin and Swift also don’t subscribe to JavaScript’s “no breaking changes, ever” philosophy which allows them greater flexibility and efficiency. Additionally, iOS and Android provide extensive component libraries with polished, accessible components that users have come to expect. Web is improving in this area lately but it has some catching up to do.
For smaller teams, maintaining separate Android and iOS apps often is a time sink and a functional bottleneck when knowledge isn’t easily shared within the team. Plus, users switching between platforms would need to relearn the UI entirely. Today’s cross-platform frameworks, like React Native and Flutter, provide alternatives to native apps, each with different approaches. Although our team of frontend developers was more familiar with React, Flutter ultimately proved to be the better option due to its reliability and speed of development at the time (thanks in part to the amazing documentation and helpful community). Moving to Flutter was straightforward, partly because Dart (the language it uses) — designed over a decade ago as a JavaScript alternative — felt both familiar and powerful.
All in all, I can definitely see the value in all three options: mobile web apps, native apps, and cross-platform solutions, depending on what the project’s goals, user base, and resources are. So it’s one of those “choose the best tool for the job” situations.
As an education mentor, you helped over 100 students with frontend development. What are the most common challenges you’ve seen new developers face, and how did you help them overcome these?
Each student has their own strengths and weaknesses, so this isn’t a one-size-fits-all answer, but I’d say the most common challenge for beginners stems from figuring out the right level of abstraction to work at.
This is something almost every developer, at any experience level, struggles with — on a smaller or larger scale — on a daily basis. It gets easier with experience, but for beginners, it can be particularly difficult. From my experience, there are two extremes that beginners often fall between, which I call “Deep Divers” and “Entrepreneurs.”
Deep Divers feel the need to have total control over the software and understand every detail, almost down to a physical level. If you ask them to write a “hello world” in a new language and leave them alone for a day, you’ll often find them reading about compiler internals or the history of assembly, without having written a single line of code. Alternatively, they might bombard mentors and AI assistants with questions, without even attempting to implement a solution before they are fully confident in the approach — something that often never comes. Or, they can develop a habit of “reinventing the wheel”, hesitant to rely on production-grade solutions because they feel they need to understand everything completely, but they don’t have the time to invest in that level of detail.
On the opposite end, we have Entrepreneurs, who are able to produce a working program almost instantly, often by copying code from Stack Overflow or prompting an AI assistant. However, they don’t always understand what the code is doing or the trade-offs involved. In today’s world of rapid AI advancements, it can be tempting to rely on AI to do the coding for us. To some extent, this might work at a junior level, but the reality is that junior devs are hired to get familiar with the system, learn from tasks, and grow into mid-level and senior positions. In most cases, extensive use of AI becomes a limiting factor that hinders a developer’s growth. There’s another group of Entrepreneurs who believe all complexity can be abstracted away by using third-party packages. When something breaks, they blame the current solution and try to rewrite everything from scratch with the latest and greatest tool. This often leads to wasted effort when a quick, five-minute analysis could have solved the problem.
Both of these mindsets can strain a developer’s psyche. Junior devs often find themselves in a defensive mindset (“Why don’t they appreciate me? I know more than others / work faster.”) or a defeatist one (“Why am I working slower / know so much less than others?”).
The tragic thing is that it’s difficult to explain what the “right” level of abstraction is. Some tasks require deep dives, while for others, speed of development trumps execution speed or even readability.
The good news is, you don’t have to find the perfect balance; just one that’s good enough for your education or current work. Personally, I’m a huge proponent of prototyping: get something working first (ideally using your own knowledge and existing codebase, but leveraging search engines or AI assistants when needed). The key is to make sure you understand every line of code, and then refine it to make it more efficient, readable, and maintainable.
Ultimately, one of the biggest mindset shifts for a junior developer is realizing that they’re not just there to write code; they’re there to help solve business problems — both current (feature-rich products, speedy delivery, low costs) and future (managing tech debt, ensuring security, etc.).
As an educator, I always take a practical problem-solving approach, encouraging my students to work on real-world projects where they can learn by doing. I offer as much hands-on mentorship as possible, especially when they get stuck. More often than not, I guide them to ask the right questions and encourage independent research, because that skill is just as important as coding itself.
You’ve participated in and been a runner-up in several international hackathons. How has this experience influenced your regular work, and what skills do you think are crucial for success in hackathons?
Great question! This ties in perfectly with the previous one. As I mentioned earlier, finding the right balance between delivering something quickly and maintaining understanding and control over it is a common struggle. Hackathons turn this up to 11, as you’re often working under tight deadlines to deliver an over-ambitious project.
I think hackathons are where I learned how effective prototyping can be. The key is to focus on building something functional, even if it’s rough around the edges, and refine it later. It’s a mindset of progress over perfection — get it working first, and then iterate and improve. This iterative approach has influenced my regular work, where I prioritize getting something to UX testing quickly before worrying about perfecting every detail.
Hackathons also heavily rely on clear communication and adaptability. Everyone needs to understand their teammates’ strengths and what each person can deliver in the time available. More importantly, you need to be ready to pivot at a moment’s notice if something falls through or if your initial assumptions aren’t holding up. You might be forced to completely change direction or come up with a creative solution on the fly, which teaches you to stay flexible and focused on the goal.
In hackathons, time management is absolutely crucial. You’re working under extreme time pressure, and there’s no room for perfectionism or unnecessary detours. This forces you to prioritize tasks and break down the project into smaller, achievable goals. It’s a great exercise in making fast, smart decisions, while also managing expectations of what can realistically be done.
Ultimately, hackathons showcase how speed, experimentation, and flexibility are critical to solving problems quickly and effectively. They make you comfortable with failure and iteration as part of the process, and force you to sharpen your time estimation skills and learn how to deliver on time, even when the clock is ticking.
At Signal, we run internal hackathons where developers, designers, and product people come together in small teams to deliver prototypes for “moonshot” projects. I absolutely love this approach and see a lot of value in it. It gives you the chance to collaborate with people you don’t typically interact with, dive deep into problems that may not relate to your daily tasks, and experiment with new technologies without feeling the pressure of immediate production deadlines. In fact, at the last hackathon, I even had the chance to put on my prompt engineer hat, which was a great way to stretch my skills in an area I don’t usually focus on.
In your work at Yandex, you created a framework for generating GraphQL queries from TypeScript decorators. Can you explain the benefits of this approach and any challenges you faced in its implementation?
I had the opportunity to work on many exciting projects at Yandex, but creating the framework for generating GraphQL queries from TypeScript decorators was particularly rewarding and challenging.
The primary benefit of this approach was significantly reducing boilerplate for smaller Node-centric applications. By using decorators on class fields, we were able to define frontend and backend types, SQL fields (via TypeORM), and transport-level queries all in one place. This enabled us to build a streamlined and cohesive data flow, where the same localized logic could handle mapping, aliasing, data hiding, and even property-level access control.
This approach not only ensured end-to-end type safety but also minimized repetitive code. My favorite aspect was its impact on onboarding and consistency — since the team was already familiar with TypeORM decorators, the learning curve was manageable. It also established a clear “right” way to add new tables, fields, and authorization rules, which made code reviews faster and more straightforward.
The challenges were equally noteworthy. One major hurdle was the limitations of decorators at the time — they were still an experimental TypeScript feature, which introduced type safety issues. As a result, the framework’s implementation was more complex than I initially hoped, requiring extensive testing to handle edge cases correctly.
Another challenge was performance optimization. I needed to move as much logic as possible from runtime to build time and implement aggressive memoization strategies to ensure the framework remained efficient.
Finally, future-proofing was another key consideration. To address this, I added support for custom adapters at both the database and transport layers, which e.g. simplified implementing live updates through WebSockets.
Ultimately, this project was not only valuable to the product and the team but also an incredibly enjoyable and educational experience for me personally.
You’ve worked with a variety of frontend technologies throughout your career. How do you stay updated with the rapidly evolving frontend ecosystem, and how do you decide which new technologies to adopt?
The first part of staying updated is relatively straightforward for me: I rely on several weekly newsletters (covering JS, Node, CSS, UI/UX, and others), YouTube channels, GitHub subscriptions, meetups (both online and in-person), and knowledge-sharing within the frontend guild at Signal.
This may make me sound like a bit of a luddite, but I’ve consciously decided to avoid social media platforms like X, Bluesky, Twitch, and others. While I know I’m missing some insightful discussions, every time I tried to keep up with them, it felt like drinking from a firehose.
When it comes to deciding which new technologies to adopt, beyond the obvious markers like community support and maturity, I’ve learned to pay closer attention to how well new tools integrate with the existing stack and the potential tech debt they could introduce. One of my key priorities is keeping complexity to a minimum, so I tend to err on the side of caution when evaluating new options.
I generally prefer smaller, focused tools that solve specific problems rather than larger, all-encompassing libraries. That said, if the product is built around a certain tool, I recognize its value in jumpstarting development, though it may come with its own set of challenges.
Lastly, I like to experiment with new tools in smaller, low-risk projects before introducing them into production. This allows me to ensure that the technology not only addresses immediate needs but also integrates well into the team’s workflow and aligns with the product’s roadmap.
Your experience spans frontend, backend, and mobile development. How do you think this full-stack knowledge benefits you in your primary role as a frontend engineer?
Having varied experience has given me a broader perspective, which proves invaluable in my day-to-day work.
This range of experience allows me to better understand the full context of the projects I work on, especially when building and optimizing frontend applications that frequently rely on a Backend-for-Frontend (BFF) architecture.
In practice, my backend knowledge enables me to collaborate more effectively with backend teams and independently handle most BFF-related tasks. Being able to design both UI components and API endpoints reduces friction in the development process. It allows me to deliver features end-to-end without blocking others or being blocked myself, significantly improving the speed of iteration.
From a mobile perspective, I’ve learned to tailor web applications for a wide variety of devices and address the unique challenges mobile clients face when consuming APIs, such as the need for non-breaking changes to accommodate slower app update cycles.
Ultimately, I think of myself as an engineer first and a frontend developer second. This full-stack experience equips me to make more informed decisions, collaborate seamlessly across teams, and maintain a holistic view of the product, leading to more cohesive and well-rounded solutions.
Looking ahead, what emerging trends or technologies in frontend development are you most excited about, and how do you see them shaping the future of web and mobile applications?
There are a lot of things to be excited about!
I really hope to see a wider adoption of new HTML and CSS features as they arrive in the Baseline, my top picks are <dialog>, <popover>, anchor positioning and scroll driven animations. These make it easier to create things like modals and tooltips natively, without relying on heavy libraries, and they come with built-in accessibility and better performance. It’s a huge step forward for simplifying frontend work and user experience.
What’s even more exciting is how these features tie into the rise of headless components. With native tools handling positioning and interactivity, headless libraries can focus purely on logic. This lets developers build fully customized UIs faster and with less code, while keeping everything accessible and flexible. It’s the perfect match for modern design systems.
And then there’s AI, which is fascinating both for developers and end users. We’re starting to see AI running locally in the browser, thanks to WebGPU and WASM. That opens up privacy-focused features like real-time transcription or translation without needing a server. On the dev side, tools like Copilot are already changing how we code by speeding up repetitive tasks and even debugging. These trends together are making frontend development faster, smarter, and more creative.