Sunday, 2 July 2023

Fresh Interviews


Lately, I had the chance to shadow and have interviews with fresh graduates, which was a great experience for me, I notice common mistakes that the interviewers keep telling the interviewee about it, and here I am trying to give the fresh graduates some generic common advice, most of them are basics, but people keep failing in them.

Why interview?

The interview isn't a simple process as there is a question, if you answer it correctly "You pass" or else "You failed", while part of that is true, but it's not as simple as that, good interviews have many things that affect the total outcome. If the company just wants to solve that problem it can just be sent, with no need for all this extra cost, so having interviews means that the company doesn't really focus on the solution, which means that you can solve the problem and got rejected or struggle with it and got hired.

A proper Interview process is hard to accomplish, having long and different interviews is tricky but when doing them right it will be guaranteed that the remaining candidate will have higher quality. The interviews cut from the employee's working hours to prepare, perform, and examine it, this process by its nature cost the company money and time to find someone that may join.

Comparing the Candidates is a tricky process as the company may have candidates with technical levels so close to each other and only one free position. What should they do in this situation? Working with a binary system (hire/no hire) means the company will pick FIFO, also that system will make it hard to have more than one interview, and when it comes to that company should handle some cases, like someone caught cheating in an interview or done very bad behavior with an interviewer, cases need to be handled smoothly in the hiring system, hire/no hire means many things.

Some Companies follow the level or the area system, in the level system each interviewee writes his outcome as (Strongly no hire, no hire, weak hire, hire, strongly hire), and each of these words is related to the hiring policy, similar to that some companies can divide the feedback into some areas (green(+1), yellow(-1), and red(-100)), and only candidate with a score greater than x will be considered.

Candidate earns and loses points without noticing, he/she thinks something is no big deal like ignoring instructions while it costs many points even if he/she manages to solve the problem, things like answering clearly about why we can't solve the problem using x give points.

Common Mistakes in Interviews

Most of these mistakes are easy to avoid after some interviews, I encourage you to get as many interviews as possible and never be down when you fail in an interview.

  1. Silent solver: you need to talk about what you think, the possible solutions, and the complexity of each of them, thinking in silence will make the interviewer give you wrong hints, and he will assume you can't solve the problem using x algorithm because you didn't mention it, and possibly you saw the problem before and want to fake thinking.
  2. Only optimal solution: giving a problem, the interviewer expects you to mention the brute force or some bad solutions first then try to improve them, not saying these solutions may mean you can't even solve this problem with a bad solution. 
  3. Ignore hints: when an interviewer gives you a hint it is for helping you, following hints should end you with a solution or give you some point, but ignoring them is a very bad indicator.
  4. Fitting solution: many fresh interviewees focus on getting an "accepted answer" based on codeforces.com or another website so they will ask something like what is the value of n, when the interviewer says "find the optimal solution" means that the best big o, if you give him an o(n*n) solution it can be a bad solution because optimal solution runs in o(log(n)).
  5. Assume things and never ask about them: some interviewers will not say everything, to see if you can find out the optimal solution for some cases or handle the corner case, for example, someone will say "number" but didn't tell you it's integer or not, some may not mention the data value range is not large (only values from 1 to 100) asking about these things give you points and may end you with a good solution.
  6. Asking the wrong questions: what is the time complexity of the optimal solution? What is the interview outcome? Where are the places you get the question from? ...etc. when interviewer ask you about your question he/she expect you to ask about the company, or the working environment.
  7. Not listening to the interviewer: things the interviewer states very clearly like "Let's move from this point" or "That is enough here" are things related to the interview process, so ignoring them and deciding to keep going is an indicator that you don't listen to the other.
  8. Zero info about the company: people can have little info about the company in the primary scanning but reaching an advanced stage without knowing the basics about the company indicates that they don't care whether it's a good or bad company, has a strong technical team or not ...etc., people who join a company with zero info about it more likely to leave it asap.
  9. Questioning the interview process: normally the interview process isn't perfect, but it's something other people put many efforts into it, so asking questions like : why did you ask me this question? Does it really matter? ...etc. is an awful thing to do.
  10. No interview preparation: always try to be in your top form before the interview, you need to read about related topics before the actual interview, relax, and push tasks after the interview.
  11. Weak CV: having a CV without your projects, GIT account, LinkedIn account ...etc. make it hard for people to know more about you from your CV and consume some time at the interview to ask about unmentioned info, check careercup.com/resume for more info.

The summary

At the end of the day the interview is just meeting with someone older than you, just be a polite person and try to show your good side, 99% of the time the interviewer will not try to make you fail or give you wrong hints, some may try to trick you to test the technical fundamentals, but the majority will keep it straight forward and try to help you.

Being rejected in an interview isn't necessarily a bad thing, ask for feedback to improve yourself and focus on the next interview, there are many reasons why you don't pass the interview so don't assume things that are out of your control.

Also, never assume that if you are good you should pass all interviews, maybe the position needs different scope or the interview process at that company isn't good enough.

Books like "Cracking the Coding Interview" really helped me do better in the interviews, also here are videos (in Arabic) that may help you (some may be Generic for all levels not only fresh) 

Finally, good luck with your next interview.

Saturday, 29 April 2023

SDKs

What are the SDKs? 

SDKs refer to the software development kit. It is generally ready-to-use code published as a package that you can install and use with a tiny effort.

There are many use cases for that, for example, when you implement a UI unit that the company will use in many projects it will be faster to push it in a package so everyone on the other projects doesn't need to implement or copy-paste it. Another example is when a company provides service for other companies it will be much faster to publish an SDK that contains all functions provided by that company rather than talk to each company, send long documents with DTOs, APIs, endpoints ...etc. and have meetings and assist each new company.

There are other package types, for example, the framework you code with is just a group of packages, or even the applications you use on your devices, but for simplicity let's talk only about the SDK.

Why? Many of the programming problems you face today someone faced yesterday. To prevent things like duplicating code on many projects, the headache of updating all code copies and to speed up the integration process between different software components.

Package versioning 

All packages contain a header to define them, the header contains useful info like who publishes it, the targeted framework ...etc. but the most important thing is the package name so you can install, define and use the package in your project, and the second thing is the package version number.

The package version number is the tricky one, you will need to understand it deeply (will know why in the dependency resolution section). As there are always new things in the software development world, any package has to keep updated with these changes. This will result in having different versions of the packages so what is the problem? Let's just give it an auto-increment id so the users know the latest version. Actually, having single digits will make the difference between versions too ambiguous, there is no guarantee that each version has compatible issues or not, is it just a small fix or a major change that we need to upgrade the framework and check our package interface. To solve these problems let's take a look at Semantic Versioning.

Semantic Versioning (SemVer)

It's a standard versioning system to define the package version, firstly it states that the package number contains three elements separated with "." in the following format: MAJOR.MINOR.PATCH, Here you can find the definition of the three elements:

  1. MAJOR version when you make incompatible API changes
  2. MINOR version when you add functionality in a backward-compatible manner (will explain later)
  3. PATCH version when you make backward-compatible bug fixes

There are many rules but the ones you will need to be aware of them:
  1. Each element contains only natural numbers without leading zeroes. 
  2. Each element MUST increase numerically. You must set the elements on their right with zeroes when increasing an element. (1.11.23 -> 2.0.0 and 2.4.23 -> 2.5.0)
  3. The contents of that version can't be modified after the versioned package has been released.
  4. A pre-release version is defined only by stating that on the rightest part using only ASCII characters. (1.0.0beta)

The incompatible changes

Sometimes you will upgrade the package version to add logs for example, these changes will have zero effect on code usage as the outer packages will not care about that implementation as long as everything runs smoothly, whatever you use the older or the newer version the outer package will not have any compatible issues.

But other times you will need to change something more critical something like an interface, or model data, in these times if the project uses an older version it will crash on the runtime saying for example (I want an interface with three functions but the package hasn't it).

So, these types of changes are stated as Major and if the project has a package with 2 major versions it will just tell you to resolve this issue before running the project and that is exactly why Dependency resolution.

Dependency resolution

The former definition of it is the process of finding and installing the correct version of a package and its dependencies. before diving deep let's show a complex state that will highlight the problem 

Normally packages can use other packages. but there is no guarantee that all packages will use the latest package version. Imagine you publish package A and then package B, and package B use package A with version 1.0.0, lately, you needed to update package A to have more features to use in package C, and package C use package B at the same time, the question here what will be package A version?

You can check the following diagram for another example:

Most of the time the package version graph contains these samples:

  • ">=x" means Developer doesn't care which version you use as long as it is greater than x and less than the next major change, generally speaking: the package manager gives you a warning for incompatible versions.
  • "=x" means you can only use version x else you need to update the package even for tiny changes. This style has limited use cases; generally, don't use it unless you need to.
  • "=x.*" means use the latest stable version with the prefix package number x. This option will remove the headache of manually upgrading all other packages and repo each time you release a minor or patch change, this also can be customized to accept only patch changes.

There are some common rules here:

Lowest applicable version: that state that the project will use only the version that fits with the highest package version asked without upgrading to the latest unless using float point 

For example, if packages ask for package X with versions (1.0.0, 1.2.10, and 1.2.1) the project will select version 1.2.10 in the whole project even if package X has a newer version (for example 1.3.0).

Direct dependency wins: When the package graph for an application contains different versions of a package in the same subgraph and one of those versions is a direct dependency in that subgraph, that version would be chosen for that subgraph and the rest will be ignored. 

Sometimes this will result in forcing an update, for example, if you use package Y that uses package X with version 2.0.0 but you told the project to build with package X =3.0.0 or =1.0.0 the project will ask you to solve to that version 2.0.0 or go to the package Y and upgrade package X.

Other rules depend on your use case and the framework and I want to keep it as simple as possible.

Resource:

Semantic Versioning 2.0.0 | Semantic Versioning (semver.org)

NuGet Package Dependency Resolution | Microsoft Learn

Monday, 20 February 2023

Assume Breach

Assume Breach is a mindset, mentality, or group of questions that target minimizing the damage of any cyber-attack if happened.

The original mindset for security is to prevent any outer attack, on another side, AB looks at a cyber-attack from a different angle, it tells the programmer the attack will happen and somehow manage to go through the outer security layers of the organization then ask a very important question: what will be the damage in that case?

Some will think that we should just make sure that cyber-attack will not go through layers instead of assuming it will happen, but according to cyber-attack history even when an organization adds many strong layers of cyber security the attack somehow can go through them.

So, AB asked the security of the organization, assuming someone managed to go through the security, what damage will the organization take? this question generates many questions like:

  • What is the critical info he will be able to reach?
  • How much time it takes to remove the damage?
  • Is the access and permission given to each one reasonable? 
  • What will happen when someone gets permission? 
  • When will the organization discover the attack?
  • How can an organization mentor the critical info and permission? 
  • Are the organization have a good policy that keeps track of actions and permissions?
  • Can the organization detect strange actions and eliminate their reasons?
Nowadays there are many third-party tools that help organizations and make these things much easier but the challenge here is that small companies don't think it is worth investigating in these tools at the beginning and as time passes the introduction to these things became much harder.

Saturday, 3 December 2022

Functional Programming

While OOP has dominated the programming scene for years, the term "Functional Programming" is back to life lately to solve some of OOP's problems, so let's talk about it.

What is Functional Programming?

The main concept here is to isolate the state (data) and the logic (methods), in OOP the programmer defines the object (data) first and then defines its method, in Functional Programming the programmer defines the function and the data separately.

All functions inside the system will be stateless and deterministic (predictable or pure), which means its output will be constant for the same input, it will not depend on the state of the system and will not change it, it will just return a new one.

Let's have an example, in a normal system function like getCurrentUserAccessToken() will act differently based on the system state, if the user has already login it will just return a value, if not but the user login on that device before it will try to read a stored refresh token and change system state to get the new AT, if not it will change the state of the system to ask the user to login.

Maybe these things help understand and modify the code, but at the same time, it will make the system harder to test, prove it is correct (acts correctly at all states), and determine how the input affects the output. that will result in extra effort in both testing and modifying any function.

Functional Programming is so clear at this point. for function f with the output y1...ym must be the same as long as input x1....xn didn't change, no matter how many times someone run it or what was the system state at each run, something like that means that function can't fetch any global variable, can't assume there are variable with init state, can't change the system state, and can't use external non-deterministic function (like a random function) inside the function body as long as these functions affect output.

Functional Programming will not change the current state so for example if the programmer passes an object to it the function will not change anything in that object it will return a new object as output, also global state will never change.

Functional Programming makes it a lot easier to approve the correctness of a function, and the correctness of a function makes it easier to debug code and isolate the buggy parts from another.

After the Programmer proves function correctness, he can use it at any place without worrying about if it will do the same or not as it didn't depend on the system state, he will not worry about this function changing the system state to an unhand-led state. All it sees is its input. Considering this fact Functional Programming makes it a lot easier to use a teammate's code, the programmer will not worry about unexpected behavior due to things he wasn't aware of it.

Other things, if you are worried about performance, you will be sure this function's complexity will always be determined by only its input, not hidden factors you didn't see.

Friday, 21 October 2022

Software monitoring

How to Debug Production Code?

In real-life applications most of the time debugging will not be an option, developers will need data about what happened, when, and where, debugging an application with many flows and states will take forever until found a bug, also sometimes these bugs fixes contain non-technical part (for example refund for orders that failed to deliver).
Application monitoring is divided into two parts: server-side and client-side.

Server-side monitoring

Mainly it is the most essential monitoring you will need to handle because it contains all critical things like databases, security methods, business logic ... etc. there are different types of monitoring: 

Infrastructure monitoring: Target database health, hardware usage, service/micro-service health ... etc., if something happened to these things it will affect the whole business.

Also, it targets some business concepts like response time and the number of requests/secs to certain APIs, which helps in understanding if the application needs to scale up, or if there is a problem that prevents the users from calling APIs.

These incidents are usually triggered by time (application scale, old hardware that needs to change ...etc.), but in rare cases, the problem root could be code or business changes (code reviews and tests should prevent any change that triggers these events).

High Priority incident:  Most applications have invalid states that should be unreachable (like request pay without sign-in, signing without signup, trying to insert a null value at a non-nullable column ... etc.) if any of these states became reachable for any reason it needs to fix immediately. The root of these incidents is recent code changes or bad design that didn't handle this state.

Event Logging

Event Logging is mainly used for debugging purposes to understand what happened, Logging info is stored differently than the normal data as it is much larger, it will be written once (no update operations), and contains more technical information that later (in many cases) will never be used in business, later on, these data will be moved to storage with high latency (an archive) and some applications models will delete these data.

Auto logging: most projects define a framework for auto logging, it provides the developer with standards info like API x called with parameter y, or API a returns result b, while the developer sometimes needs more specific logs, the auto login has a big advantage as it all has the same format style and it doesn't rely on the developer memory (remember to add logs).

The only thing developer needs to be aware of for logging (logging in general) is to never log sensitive data, for example, imagine an application that logs logging info (password) or payment info, while the logging info can be accessed by anyone in the company or the technical team.

Client-side monitoring

Application Monitoring is mainly used to detect a problem ASAP, keep track of what is happening in real usage and understand how the users interact with the app.

While most data collected on the server side is for technical purpose and target only the developers, the client-side data target many teams (developer, product, data analytics, design ...etc) which means this info needs to be written as clearly as possible, assuming someone who never wrote a single line of code will look to these data is the normal case here.

One thing to keep in mind is that any data that come from the client side isn't fully trusted as anyone can edit the APK and send fake data, so, for example, something like if the user paid for the service or not is something you will always look into server-side monitoring data.

Application instrumentation is the process of adding code to your application so you can understand its inner state. It measures what code is doing when it responds to requests by collecting data such as metrics, events, logs ...etc. An instrumented application tracks as much information as possible about the service’s operations and behavior. It provides more detail about what is happening, so you can see relationships between requests. 

Instrumentation is key to ensuring the application is performing up to its best. It focuses more on the whole picture rather than single info, it targets to produce something like flow, relations, charts ... etc. rather than user x sending request y with parameter {a,b,c}.

Friday, 14 October 2022

Design Principles: Encapsulation

I love to talk about one important concept that is easy to apply but greatly improves code readability and makes the code easy to manipulate in future Encapsulation.

The Encapsulation design Principal idea is simple: don't mix dynamic code with static code.

Let's say an example to understand that: imagine you write a code for an online store that sells 3 types of t-shirts, in this example, something like system order flow will be something that will not change frequently while the number of products, their names, and other things that related to the products is highly likely to change by time, so when you tightly coupled them with other logic like the order flow that shouldn't change easily will make the code so hard to manipulation in future or even to understand.

Many fall into a common misunderstanding here (the fake scaling view): in our example, a developer will think "Oh it's just used in one place and it's only 2 or 3 lines" or "It's only duplicated once", take a look at the business model for a minute and you will realize that 3 can grow to hundreds or thousands in few years then imagine a function that handled all these cases and does other business logic and all these cases are repeated multi times in different places in a single class, it will be a nightmare to any developer to try to manipulate or debug these thousands of lines.

Some may hear about over-killing and think it's alright for now to ignore that and push this concept after the product growth, but unlike something like microservices, the Encapsulation design Principle doesn't add complexity to the code it just told the developer to export dynamic code outside the logic, and a long side with single-responsibility design Principle they told the developer to break the logic into small code blocks as much as possible. 

Ignoring this concept will result in a code with a high chance of bugs in the future for two reasons:

  • The next developer who wants to manipulate the code will not understand the difference between static code (that shouldn't be changed without deep thought and proper design discussion) and dynamic code that changes frequently (the solution is to move to DB if possible).
  • Dynamic code usually exists in multi-places, in most cases that will result in changing in some places and forgetting the rest, and no one will notice that (except the person who wrote the original code), which results in many times inconstant behaviors.

This Principle is also related to other concepts like the (DRY) Principle "Don't repeat yourself" which mainly told the developer to avoid duplicate code.

One of the old great pieces of advice I heard when I was a student is: "When a developer writes a function that exceeds ~15 lines (this value is related to the language) he/she probably can divide it into 2 or more function" while this can be related more to the single-responsibility design Principal concept however understanding how to put the breakpoints in your code like "these 2 blocks of code can/should be separated" is something that also related to Encapsulation Design Principle.

Saturday, 8 October 2022

Biometric Authentication


What is biometric authentication and how does it store my data?

Let's talk about one of the recent sign-in methods that became popular recently, the Biometric login which includes fingerprint, face id, voice recognition, and other methods, but mainly let's talk about the most secure one of them, the fingerprint method.

Why are we talking about fingerprints, not face IDs or other methods?

Mainly the other methods have weakling that to recognize the real user vs a digital copy, you can have any person's voice from online videos or records, you also can get almost any person's face from internet, the sign-in system can ask the user for specific things like making a random specific reaction or say the displayed sentence, however, someone still can break this using train ML model that talks like any person or display any face reaction using public data.

The fingerprint is the only thing that proves it's the real user, but if we use fingerprint doesn't that mean fingerprint will be soon shared data if leaked?

Actually, how fingerprint works are really interesting as it's too similar to the hashed password concept but device-based instead of server-based, what happens these days is there are no public devices that store fingerprint as raw data, all devices hash the fingerprint using a unique key before storing it so if a device got hacked there is no fingerprint to steal, and the hashed value is useless as it is different based on device and the device itself doesn't know what was the real value.

For the password when the company feels it's leaked, they will ask the user to change it, the fingerprint will not have this option, so fingerprint leaking will be something that can never be recovered for the user.

So how does the online business use a fingerprint without knowing the fingerprint of each user?

What happened is the device contains most of the time a token generator and when the device verifies the fingerprint it will just return a new token to use instead of the hashed value, Biometric auth is just a way to verify the user who is using the device right now, there are also applications for auth devices that only activate using Biometric auth.

This explains why the user needs to log in first on the device to enable Biometric to auth on each new device even if the user already enabled it on other devices, Biometric auth support only one user on the device for now because the applications do not have access to hashed values it just asks device and the device return true or false (or auth token if it's auth device).

image references: [link1]

Multi-tenant pattern in SaaS platform

In the last year, I had the chance to work on an interesting problem for a SaaS platform that serves as a B2B2C platform (business-to-busine...