Tough situation examples

During one of my interviews, questions appeared about my experience resolving tough situations in a team.

I have much experience working with teams, but I used to work in a toxic environment. So, any toughness was normal. Anyway, I remember some good examples.

Continuous struggling

As part of a backoffice in Worldline, one of our responsibilities is reconciliation, so we parsed reports from the partner.

My team moved a region to accept payments from legacy to a new system. Meanwhile, our partner updated their platform. During development, testing and piloting in production, everything was fine.

When we went live, we started to receive every day absolutely different data in the files from our partner. The team had to pre-reconcile them manually.

During the first weeks, I identified stakeholders using the RACI model and escalated the issue to the C-level, creating meetings to inform and consult with our bosses. Finally, I got confirmation that our partner send us reports in their new standard, and they are not willing to fix it.

At the same time, the teammates who were working on the manual pre-reconciliation were more and more demotivated every day. I kept transparency so everybody knew that it was not our fault, and a lot of people tried to solve the issue with their partners.

From the first day of the issue, I was looking a way to implement processing for the new files in our system. I proposed to the team to get rid of the hope that we'll receive the expected data from our partner.

We decided to make weekly shifts for the manual pre-reconciliation, and the other teammates developed a solution to reconcile files automatically. Also, I offered to start the resolution development creating little programs-helpers to reduce manual efforts first. It additionally gave us the ability to test implementation ideas very quickly.

In two weeks, we reduced manual efforts from 12 to 5 man-days a week. Then, we were building a new solution, and in 2 months, the manual process required only 15 minutes a day.

The whole team was working together and had only one goal—to eliminate manual work from teammates as soon as possible.

Finally, I created a set of retrospectives with all participants in the project to identify actions that will help us mitigate the same issues in the future projects.

Underperformance new-joiner

When I was working in Sberbank, I hired a very talented Java developer. He met all our expectations. But after the probation period, the whole team noticed that he came to the office being exosted and without any desire to work. The whole team couldn't understand the problem, and the new-joiner didn't want to share. He just kept silent, while all his objectives were failed.

I wanted to keep him on the team because he was an excellent developer, and we had a hiring freeze at that time. I proposed to my manager that we give him a chance.

I discussed with the developer and finalized that he is too bored with his daily routine. He just arrived in a big city without any idea what to do at non-working hours; meanwhile, as usual for new joiners, his tasks were too easy and tedious.

  • Together with the team, we assert to him some challenging tasks;
  • I got a budget for courses to keep him busy and expand his competence;
  • We had weekly check-ins to measure his engagement by the level of confidence.

As far as I know, he is now one of the strongest engineers on the team. I know that it worked because the developer wanted to stay and just didn't know how to improve his daily routine. But for me, it was a very interesting challenge to give a chance and to build an agreement between a person and a team.

Security impediments

At Sberbank, it was difficult to communicate with the security and architecture departments. Through a delivery process, we demonstrate every feature to security and architecture departments.

The security officer attached to our team was a completely non-technical person. The team couldn't understand his requirements, and our explanations were too hard to comprehend. I tried to find a connection point with him, including a set of 1-2-1 knowledge transfer sessions that I made special for him. Meanwhile, I tried to help the team understand his point of view.

The whole team was so mad about this guy's behaviour. I taugh the team not to judge his personally, but reflect on his behaviour to understand better his point of view.

With the team and the officer, we were keeping a cadence in our activities to eliminate misunderstandings.

Finally, we got a very helpful security officer.

Am I sure?

What are the duties of an engeneering manager? Why a company need this role? And the most important question for my is am I understand what I would do and want it?

To help me navigate a career challenge, my wife recommended that I find a mentor. I searched for mentors on Google and came across mentorcruise.com. After applying the necessary filters, I found a mentor named Andrei. Luckily, he speaks Russian and has a similar background to mine. He has been working as an engineering manager for over four years.

Andrei recommended a series on being an Engineering Manager by Matthew Vanderzee. While I was reading it, I found that I agreed with almost everything in the articles.

Additionally, he recommended me a James Stanier's book "Become an Effective Software Engineering Manager: How to Be the Leader Your Development Team Needs". The book is amazing, I'm going to make some of my notes based on ideas arises in my mind during reading.

Let's have one more attempt

I have chosen a career path to become a CTO through an Engineering Manager role in a corporation. Of course, I know that the easiest way to become a CTO is to create your own startup.😁 Finally, I want to achieve the desired role by working in a big company with a huge responsibility and impact on the world.

In my career, I have always had doubts. I enjoy working on technical tasks and writing code for fun on weekends. At the same time, I do not like writing code for money. Instead, I prefer managing teams, organizing people, and improving the effectiveness of developers to build big ambitious products.

So, after some setbacks, I have decided to systemize my approach and set a goal for myself. By the end of/a9vc 2025, I will occupy an Engineering Manager role in a big company with a decent salary.

This does not mean that I will only write about my journey to this role. Sometimes, I'll write about the technical knowledge that I gain while working on my hobby projects, which might become little startups in the future. 😁

Big-O notation

Big-O notation categorizes algorithms based on efficiency. It helps us compare and choose the best algorithm for a task.

The Essence of Big-O

One way of finding the largest number in a list involves comparing each number to the current largest one. This is a linear time algorithm, shown as O(n).

Another approach involves dividing the list in half, finding the largest numbers in each half, and comparing them. This is more efficient and represents a logarithmic time algorithm, shown as O(log n).

Understanding Big-O notation is important for several reasons:

  1. Performance Optimization: It helps predict the impact of increasing input size on algorithm performance.
  2. Algorithm Comparison: Provides a standardized way to compare the efficiency of different algorithms.
  3. Code Scalability: Helps design and implement scalable solutions for large datasets.

Common Big-O Notations

O(1): Constant time. The algorithm takes the same amount of time regardless of input size.

O(log n): Logarithmic time. The runtime grows logarithmically with input size.

O(n): Linear time. The runtime grows linearly with input size.

O(n log n): Log-linear time. The runtime grows slightly faster than linear time.

O(n^2): Quadratic time. The runtime grows quadratically with input size.

O(2^n): Exponential time. The runtime grows exponentially with input size.

Big-O notation gives a good idea about algorithm efficiency, but doesn't cover everything. Factors like constant factors and specific hardware also affect performance. Still, it's useful for understanding algorithm scaling and making smart choices.

Understanding Big-O helps us pick efficient algorithms, improve code, and handle big datasets.

Clean architecture and transactions in Golang

I want to narrate my approach to using transactions with repository patterns using clean architecture.

Problem

In a classic service, we have use cases separately with repositories. Use cases know nothing about databases and storage. Repositories don't know about business logic. So, which layer should raise a transaction?

I suppose the most reliable and convenient layer for transactions is a use case. Use cases may know about the necessity to run transaction. And it doesn't matter for a use case if transactions really work in repositories.

And how should we work with transactions in a variety of repositories in one use case? Let's start with a simple example. Imagine that we have pages with likes. For pages we use one repository and for like another one. And we want to remove likes when the page is removed.

package repository

type PageRepository interface {
    Delete(id int64) error
}

type LikeRepository interface {
    DeleteByPageId(id int64) error
}

In our use case, we want to delete all likes after the page has been deleted.

package example

type MySuperService struct {
   pageRepository repository.PageRepository
   likeRepository repository.LikeRepository
}

func (m *MySuperService) DeletePage(id int64) error {
    if err := m.likeRepository.DeleteByPageId(id); err != nil {
       return err
    }
    if err := m.pageRepository.Delete(id); err != nil {
        return err
    }
    return nil
}

Looks good, but where are transactions? And which of the repositories should start a transaction?

Solution

This solution is suitable when all repositories use the same databases. We add a field to the service's struct. It will be used for transaction management and for running transactions by calling this field's method BeginTx.

package example

type MySuperService struct {
   transaction    transaction.Transactioner
   pageRepository repository.PageRepository
   likeRepository repository.LikeRepository
}

func (m *MySuperService) DeletePage(id int64) error {
    // Start transaction
    tx, err := m.transaction.BeginTx(ctx)
    if err != nil {   
        return fmt.Errorf(`transaction error %w`, err)
    }
    // Execute query
    if err := tx.LikeRepository().DeleteByPageId(id); err != nil {
       if err := tx.Rollback(ctx); err != nil {
          return fmt.Errorf(`rollback error %w`, err)
       }
       return fmt.Errorf(`can't remove likes %w`, err)
    }
    // Execute next query
    if err := tx.PageRepository().Delete(id); err != nil {
       if err := tx.Rollback(ctx); err != nil {
          return fmt.Errorf(`rollback error %w`, err)
       }
       return fmt.Errorf(`can't remove page %w`, err)
    }
    // All done, commit!
    if err := tx.Commit(ctx); err != nil {
        return fmt.Errorf(`commit error %w`, err)
    }
    return nil
}

We have added a new dependency in our service and then could use it for transaction management. Service calls transaction, but service knows nothing about the repository layer's internals such as working with a database. Transaction interface looks like an abstraction for a database and should implement methods to work with transaction. And the main idea in this approach is methods in transaction struct which returns repositories with already started transaction.

package transactionimport 

type Transactioner interface {
   BeginTx(ctx context.Context, opts ...*TxOptions) (Transaction, error)
}

type Transaction interface {
   Commit(ctx context.Context) error   
   Rollback(ctx context.Context) error
   
   // Methods return repositories with transaction
   LikeRepository() repository.LikeRepository
   PageRepository() repository.PageRepository
}

Any database could be used for transaction management with this approach to transaction interface. It's easy to use and maintain.

Transactioner interface has only one method to start transaction and return Transaction interface that has methods for repositories for commit and rollback.

But if we use a variety of independent storage at the repository level, there should be another solution. Just clean and simple!