Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Noitidart/1aa3cf45460bce91eb9f0d938e52af90 to your computer and use it in GitHub Desktop.
Save Noitidart/1aa3cf45460bce91eb9f0d938e52af90 to your computer and use it in GitHub Desktop.

Frontend Focused Full-Stack Software Engineer

Type: On-site Level: L2 / SWE2

  1. (Type: Behavioral) I went through Amazon leadership principals and examples at work. He asked about community driven, customer driven products like NativeShot. He asked about tickets I got. I gave him example of bias for action where I took a low priority ticket and made it high priority without manager approval, because it was for a muti-million dollar deal. I talked about how the AWS docs were too geeky and not customer focused, and how I stuck with my "always right gut" feeling and kept a nose out. I did disagree and commit though, and didn't push until I had facts.

  2. (Type: Whiteboard Code) This actually was more design then code, even though I had a design/architecutre session letter. I had to design an async TaskQueue without using workers. Tasks should run serially, meaning it should not do next task until this task is done. He gave me it should be used like this:

    const q = new TaskQueue(50); // the number here is the minimum time gap in ms that is guranteed between tasks
    q.enqueue(() => console.log('foo')); // the callback here is "job"
    q.enqueue(() => console.log('bar'));
    q.run();
    

    He told me the only blocking should be when the job is running. It's ok for run to run the first job if available right away within the same "tick". We talked about trade-offs of various things while designing this. My solution was similar to this though:

    class TaskQueue {
    
        timeout = null;
        minIntervalMs; // set by constructor
        jobs = [];
        isRunning = false;
    
        constructor(minIntervalMs) {
            this.minIntervalMs = minIntervalMs;
        }
    
        enqueue(job) {
            this.jobs.push(job);
        }
    
        run() {
            if (this.isRunning) return;
            this.maybeDoJob();
        }
    
        maybeDoJob() {
            const job = this.jobs.unshift();
    
            if (job) {
                try {
                    job();
                } catch(ex) {
                    console.warn('Job caught exception:', ex);
                }
            }
    
            this.timeout = setTimeout(this.maybeDoJob, this.minIntervalMs)
        }
    
    }
    

    I defined here that job is a function. He then told me to support dequeue and also wanted callback to trigger with value of job. So I changed job to be an object { f: the_func, cb: (val)=>void } added:

    
    enqueue(f, cb) {
        this.jobs.push({ f, cb });
    }
    dequeue(f) {
        const index = this.jobs.findIndex(job => job.f === f);
        if (index > -1) {
         this.jobs.splice(index, 1);
        } else {
            console.warn('already dequeued'); // be silent in production, as clearTimeout, clearInterval, clearAnimationFrame
        }
    }
    // maybeDoJob() {
        ...
        try {
            const res = job.f();
            try {
                cb(null, res);
            } catch(cbEx) {
                console.warn('caught excpetion trying to call callback');
            }
        } catch(fEx) {
            cb(fEx);
        }
        ...
    

    I talked about alternative patterns to callback pattern. Observer pattern, where I would possibly return an id from enqueue. I talked about promise pattern.

    I forgot to but should have said that the callback arguments matches the node pattern of error first. This is done because to ensure as a dev he checks error happend or not.

    I also talked about supporting async functions, by throwing an await on the job execution in maybeDoJob, and await will work fine with sync functions too.

  3. (Type: Architecture)

    Design CRUD endpoints for resume entity. Then how would this design change, or what is added if we want to create a collaborative room where multiple users can edit someones resume.

    I went over rest endpoitns as outlined here - https://sailsjs.com/documentation/concepts/blueprints/blueprint-routes

    Having a Resume model in your app generates the following routes:

    • GET /resume -> find resumes matching criteria provided on the query string, using the find blueprint.
    • GET /resume/:id -> find a single resume with the given unique ID (i.e. primary key) value, using the findOne blueprint.
    • POST /resume -> create a new resume with the attributes provided in the request body, using the create blueprint.
    • PATCH /resume/:id -> update the resume with the given unique ID with the attributes provided in the request body, using the update blueprint.
    • DELETE /resume/:id -> destroy the resume with the given unique ID, using the destroy blueprint.

    If the Resume model has a “to-many” relationship with a X model through an attribute called xs, then the following additional routes would be available:

    • PUT /boat/:id/xs/:fk -> add the driver with the unique ID equal to the :fk value to the xs collection of the boat with the ID given as :id, using the add blueprint.
    • DELETE /boat/:id/xs/:fk -> remove the driver with the unique ID equal to the :fk value to the xs collection of the boat with the ID given as :id, using the remove blueprint
    • PUT /boat/:id/xs -> replace the entire xs collection with the xs whose unique IDs are contained in an array provided as the body of the request, using the replace blueprint.

    I discussed my integ tests would test the controllers by doing a request to the endpoints. My controllers only work on plicies, input validation, calling the static model methods, then output formating based on response type of json or html requested. My unit tests would be on the model methods.

    Mentioned I start with an ERD, even in frontend because most of the entities rear their head in the frontend. Example of ones that don't are like forgot-password entity.

    I forgot to talk about authentication, passing in token (JWT, OAuth) as bearer in Authorization header, or using cookie. I don't know too much trade-offs, but if you go with cookie approach you may need to implement CSRF to ensure that the user is hitting from where he says he is. Whereas the https session data will tell us the user is who they say the are.

    Trade-offs of Websocket vs Long Poll:

    • With Websocket, prioritizing state to the customer, by broadcasting the state before writing to database. Even if you async fired off a call to write to database in parallel, it is not yet gurnateed the state is persisted, but the user will see that state as it was brodcasted to him. With long poll you write to database so things are out of memory and persisted.
    • The biggest tradeoff for me with websocket is customer satisfaction over backend performance allowing for sockets.
    • Other is performance, I don't know the details about websocket perf but I want to get into it more.

    I should have mentioned but forgot to, load balancing probably for high scale, and multiple in-memory databases like redis. So this would have been mentioned in the websocket vs long poll in relation to data persistence.

  4. (Type: Algorithmic Code) Create a logger, and we want O(1) add and and we have to take the mean and we want to have best case not accessing any other elements. Later on requirement was to not hold any data that is 5 minutes old. So I talked about sweeper strategies ("sweeper" is a software term I guess). I ended up holding tail, and modifying linked list to hold prev, and would delete from tail until window satisified.

    I forgot to talk about how we can have mean keep a running value. Because he seemed to focus on space complexity.

    Then he wanted me to unit test it. I united tested sweeper method but stubbing the now function. And would stub it to return 0, then added one data point, and ensure that sweeper didnt sweep anything. Then I stubbed now to windowTime then sweeped to ensure it sweeped. All we had time for.

    He gave me

    now() // global now method that returns time
    
    class StatLogger {
        windowTime = 5 * 60 * 1000 // 5 minutes in ms
        record(value)
        mean()
    }
    

    I started by thinking about map but settled on linked list and created Node initially with no prev, but then added prev. See StatLogger.js.

  5. (Type: Manager & Interview Feedback) He told me about the team and feedback on the interview.

Questions I asked

  1. Do teams implment an agile framework?
  2. Does upper management allow for testing, because this takes time, some don't allow it.
  3. Does the frontend drive the backend, or the backend drive the frontend, or does something else drive it all like the product?
  4. Are all eng DevOps as in Amazon? How do on-call roations work for frontend teams?
  5. So the on-call is doing metrics for the backend services for availbility/faults, latency, volume. But what about frontend metrics? Frontend perf metrics? Frontend usability metrics (A/B testing users are clicking here, mousing there etc)?
  6. Are you on the team I would be on? How many members on the team I'm working on? (i asked this in the manager interview)
    • He replied all new, I responded great we can all learn together. Learning together is great synergy.
class Stat {
next: Stat = null
prev: Stat = null
data {
time: number = now()
value: number | void
}
constructor(value, next=null, prev=null) {
this.data = { time: now(), value };
this.next = next;
this.prev = prev;
}
}
class StatLogger {
windowTime: number = 5 * 60 * 1000
stats: Stat = null;
tailStat: Stat = null;
record(value): void {
const prevMostRecentStat = this.stats;
const nextMostRecentStat = new Stat(value, prevMostRecentStat);
this.stats = nextMostRecentStat;
if (!prevMostRecentStat && !this.tailStat) this.tailStat = this.stats;
this.sweep();
}
mean(): number {
const minTimeInclusive = now() - this.windowTime;
let sum = 0;
let cnt = 0;
let curStat = this.stats;
while (curStat && curStat.data.time >= minTimeInclusive) {
cnt++;
sum += curStat.value;
curStat = curStat.next;
}
const mean = sum / cnt;
return mean;
}
sweep(): void {
const maxTimeExclusive = now() - this.windowTime;
while (this.tailStat && this.tailStat.time < maxTimeExclusive) {
this.tailStat = this.tailStat.prev;
this.tailStat.next = null;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment