Isn't life interesting?
2 times now I drop the ball on an interview, mentally? not good. Depression? a little bit.
Learn a lot of how weak I am on communication, lost 2 great opportunities just because I could not answer concretely the tech questions
Lesson learnt
- Need to fix how I explain things
- Need to brush up concepts and why are we using it
These are just notes to remind myself on what kind of things you have to know
1st interview
2nd interview
-
On .NET: What is DI and what are the advantages of using it?
- Honestly my answer was a mess, answered so badly I wanted to choke myself
-
Convo History
Interviewer => So how, what do you know about dependency injection in .NET? Jiegrein => It's, uh, what do you say? A way to inject a service, uh, but created by Microsoft on .NET where you can call a service on, So you register a service first and then you can use it everywhere through injection. Interviewer => What's the benefit of using dependency? Interviewer => Why do we have to use dependency injection? Interviewer => Or can we not use dependency injection? Jiegrein => So we have, For .NET specifically, that is the way that my, that the framework was created. So I would say that you should use dependency injection. There are. Interviewer => So there's, you don't know any benefit of using dependency injection, so you just use it because that's the way .NET is created. Is there any other reason? Jiegrein => Yes there is. Interviewer => Okay. Can you elaborate? Jiegrein => Well, on dependent. Well, specifically here, there is scope, transient, and singleton, right? Jiegrein => So that's where you would differentiate what services is used to where. So for example, for caching or logging, you would use singleton because you want that to be consistent across the whole app. But for like services like DB services or unit of work, you want that to be scope where for every request, it has its own service. Interviewer => Understand. My question is why. Why you have to use it? Why not test? Can you not use the dependency injection? If you are, if you have to use dependency injection, what's the benefit? Have you ever crossed your mind? Jiegrein => Why do I have to use dependency injection? Or this is just a way like that all programmers are doing dependency injection? I'm going to do dependency injection. I know functional programming does not always use dependency injection. The one benefit I can think, wait, no. The only thing I can think of is you can decouple the services so that they don't depend on concrete implementation. Interviewer => Can you give me an example? Jiegrein => So for example, if you are to for the controller to use a service. For example, if you do not use dependency injection, then you will have to create an object of that service. Using dependency injection, then you can use an interface of that service. So that you are not depending solely on the service so that the service can be changed without changing the implementation on the controller. Jiegrein => So what I'm saying is the advantage is just the usability. I think that's the one that is mostly why. Interviewer => Okay. Any other thing? Interviewer => That's all that comes out to your mind. Jiegrein => Can be easier to test as well. Jiegrein => Yeah, those are the only things that came out to my mind. Interviewer => Okay. So does it have anything to do with the performance? Jiegrein => No. Interviewer => Any reason why? Can you elaborate? Jiegrein => Because if I've used Go and I don't think Go uses a lot of DI and performance seems to be the same. So that's how I concluded that you don't get performance benefit by using DI. -
Lesson here:
-
What is DI?
Is a programming technique in which an object or function receives other objects or functions that it requires from outside of the class, as opposed to creating them internally.
-
Why do we use DI?
We use Dependency Injection to avoid tight coupling between classes. Instead of creating dependencies inside a class, we inject them from the outside. This makes the code more flexible and maintainable because we can easily swap or extend implementations without changing the core logic. For example, if our service uses Stripe for payments and later we need to switch to Midtrans, we just change the DI registration, not the business logic. It also improves testability since we can inject mocks for unit testing instead of calling real external services. DI encourages cleaner architecture and supports the Dependency Inversion Principle from SOLID.
-
Is there a performance benefit to using DI?
No, DI is not about performance, DI containers add a very small overhead, but it's negligible, but we choose to use DI for the benefits that it gives us, like Loose coupling, Testability, and Flexibility not for speed. Most of the time DI is fine.
-
-
Note
- Injecting a concrete class is still DI, but it doesn't give loose coupling. To get the real benefits like flexibility and testability, DI should be combined with interfaces (abstractions). DI is the mechanism interfaces are what make it loosely coupled.
-
Test
- Interesting choice of questions tbh, never thought this one through
-
Lesson Here
-
Have you ever done unit testing?
Yes I have. I used NUnit with Moq. Using Moq, I mock dependencies such as DB repositories, 3rd-party APIs, or other services that are injected through the constructor. This allows us to isolate and test the business logic without hitting real external systems.
-
Does DI have anything to do with unit testing?
Yes, DI directly improves testability. Because with DI (and interfaces) we can replace real dependencies with mocks or stubs during testing. Without DI, services like DB or API clients would be tightly coupled inside the class, making them harder to isolate for testing.
-
Why is it harder to mock if you don't use DI?
Without DI and interfaces, classes create their dependencies internally (e.g.,
new SomeService()), so we can't easily substitute them with a fake or mock.The usual workaround is to change the code just to allow mocking, for example by making methods
virtualand overriding them, or exposing setters.These approaches make the code harder to maintain. With DI, we inject abstractions, so we can mock the behavior cleanly and keep tests isolated.
-
-
RabbitMQ
- Again what kind of answer is this, can't you think?
-
Convo History
Interviewer => So, I guess my first question is I saw in your resume that you have used RabbitMQ. Can you tell me what's RabbitMQ? Jiegrein => So RabbitMQ is a messaging broker and consumer type of service where so the main purpose of what I use it for is we wanted to use the program asynchronously. So without blocking the UI. On specifically RabbitMQ, I decided to use RabbitMQ on the Python app because we don't want the UI to be waiting a thousand messages Jiegrein => We were sending around three thousand messages a day. We don't want the UI to freeze three thousand messages on a day. So that's why I decided to use RabbitMQ there. Jiegrein => So then those messages will be queued and then there will be report of, okay, how many messages were delivered, how many were not. So that was the basic idea of it. Interviewer => Okay, what are the other options to make it asynchronous then because RabbitMQ is, in my opinion, is too heavy. Interviewer => There is a lighter way of doing that, right? Jiegrein => What are the other options of doing RabbitMQ? Jiegrein => I mean without using message broker and service like that? Interviewer => You can use database and put it in database and create a scheduler to retrieve it. Interviewer => Why not just do that? Interviewer => Why do you use RabbitMQ? Jiegrein => You mean like a background scheduler like that? Interviewer => That's right. Interviewer => Like a con job, yes. Interviewer => I'm just trying to find out this. Interviewer => This is not about right or wrong. Interviewer => I want to find out what is your opinion about these architectural decisions. Interviewer => I want to know your knowledge about this one, about this area. Jiegrein => So I decided on using RabbitMQ because... Interviewer => My question is why not just use scheduler like a background job, a background job, and then put it in database and then execute it every one minute or every five minutes or every ten minutes. Jiegrein => So I did not use like a background scheduler because one was a constraint by the CTO. Jiegrein => He wanted to use Python. Jiegrein => And I know Hangfire because I've used .NET Hangfire before on National List. Jiegrein => But because he wanted to branch out of .NET, I did not have a knowledge on, I did not never, I have never found, like know what is Python's background service like that. Jiegrein => And I've heard of RabbitMQ, so that's why I use that. Interviewer => So this is not about, this is not about because RabbitMQ is good with the process queuing, this is just because you, you have no knowledge what, how to process this kind of asynchronous in Python. Interviewer => Is that what you're saying? So, yeah. Jiegrein => So, the first thing that I thought of when this was the task, I thought immediately about service broker because I use, there is a background job on Ascelis, Jiegrein => where I was working before using Hangfire and there is an Azure service bus. Jiegrein => And when they, when they asked me, okay, we wanted to do this. Jiegrein => The first thing I thought was Azure service bus, but we wanted, we don't want to lock to Azure. Jiegrein => So that's why I asked about RabbitMQ. That's it. Interviewer => Okay. Interviewer => So there is no something like a, a consideration where the RabbitMQ is much better than background job or better than Azure bus, better than Hangfire. Interviewer => So there is no such consideration. Interviewer => This is just about, okay, I know RabbitMQ, then I'm going to use it. Interviewer => That's all? Jiegrein => Yes. Interviewer => Okay. All right. Moving on. -
Lesson here:
-
Why RabbitMQ instead of a backgrounder job like Hangfire?
For this case, RabbitMQ made more sense because the app needed to send 1 - 5k WhatsApp messages through WATI, fully asynchronously. The flow was controlled by a draw.io XML, meaning every user reply determined the next step in the message journey.
RabbitMQ allows us to treat each step independently through a different consumer. For example, when customer reply with Yes, we will ask for their Bank Statement though Online Login, or Uploading it on Chat. Each answer will be handled by a different consumer
This level of flexibility is harder to achieve on a simple backgrounder, and each action needs its own set of rules which is why RabbitMQ was a better fit.
RabbitMQ also gives us a durable queue system where each message is processed independently, and having retry mechanism, acknowledgment, and dead-letter handling. Which is important since on our case we don't want to lose leads by not sending messages to customer. As volume grows, scaling would be easy by just adding more consumers
-
Isn't RabbitMQ too heavy compared to a simple backgrounder?
Yes, RabbitMQ is heavier and more taxing to maintain for developers compared to a simple background job. But in my case this was required because the application needed to process customer replies, route each reply to the appropriate action on the server, and ensure the flow was resilient and idempotent. A basic backgrounder wouldn't handle this level of reliability or multi-step reply-driven logic.
-
In case for they kept pressing why RabbitMQ
RabbitMQ is heavier, but it was the right tool because this system wasn't just sending messages, it was a reply-driven workflow. Every customer response triggered a different next step, so we needed reliable, event-based processing with retries and idempotency. A simple background job like Hangfire runs inside the app process, so it's not ideal for multi-step flows, scaling consumers independently, or guaranteeing messages aren't lost if the app restarts. If this were a one-way fire-and-forget task, Hangfire would be fine, but for an interactive, stateful flow with business impact if a message is lost, RabbitMQ made more sense.
-
-
Imutable vs mutable
-
Lesson here:
-
What does immutable vs mutable mean?
Immutable means after creation it cannot be changed anymore, while Mutable means that it can be changed without needing to create new instance.
-
Why do we use immutability? What are the benefits?
Immutability helps avoid unintended side effects because the data cannot be changed after creation. This makes code safer, and naturally thread-safe since no other code can modify the object unexpectedly.
-
When do we choose mutable instead? What's the performance angle?
Use mutable objects when you need to modify a value repeatedly. If we use immutable objects for values that change often, it will create many new instances and cause unnecessary memory allocations and GC work. Example: using
StringBuilderinstead of string concatenation in a loop. -
Give a real world example of when to use immutable and mutable
- Immutable = Had multiple lenderConfiguration which contain their base address and the api key, these should be immutable so the values remain constant during runtime and are safe from accidental modification across threads.
- Mutable = There was a need to try a bank statement PDF password using the data we have, so we used their name and dob to construct several possible password on a List
-
-
TLDR
Immutable objects can't change after creation, which prevents unintended side effects and makes code easier to reason about and naturally thread-safe. Mutable objects can be changed in place, which is better for performance when values change often, like building strings in a loop with StringBuilder. I use immutability for configuration or state that must not change at runtime, and mutability when I need to update values repeatedly.
-
Top comments (0)