Mama told me not to come.

She said, that ain’t the way to have fun.

  • 1 Post
  • 5.11K Comments
Joined 2 years ago
cake
Cake day: June 11th, 2023

help-circle
  • We stay away from riddles, and instead focus on CS concepts. We’ll rephrase to avoid jargon if you don’t have a formal education, or it has been a while. Here are a few categories:

    • OOP concepts like SOLID
    • concurrency vs parallelism, approaches for each (generators, threads, async,’ etc), and tradeoffs
    • typing (e.g. is a Python strongly or weakly typed? Java? JavaScript?), and practical implications
    • functional programming concepts like closures, partial application, etc
    • SQL knowledge
    • types of tests, and approaches/goals for each

    And some practical details like:

    • major implementation details of our stack (Python’s GIL, browser features like service workers, etc)
    • git and docker experience
    • build systems and other dev tools

    That covers most of it. We don’t expect every candidate to know everything, we just want to get an idea of the breadth and depth of their knowledge.


  • We’re a somewhat big player in a niche industry that manufactures for a large industry. Yearly profits are in the hundreds of millions of dollars, market cap is a few billion, so low end of mid cap stocks. I don’t want to doxx myself, but think of something like producing drills for oil rigs and you won’t be far off.

    We have about 50 software developers across three time zones (7 or 8 scrum teams) and a pretty high requirement for correctness and very little emphasis on rapid delivery. It’s okay if it takes more time, as long as can plan around it, so we end up with estimates like 2-3 months for things that could have an MVP in under a month (in fact, we often build an MVP during estimation), with the extra time spent testing.

    So yeah, it’s a nice place to work. I very rarely stay late, and it’s never because a project is late, but because of a high severity bug in prod (e.g. a customer can’t complete a task).


  • I assume you’re talking about runtime. AddCleanup()? That’s certainly nice, but it’s not the same as a destructor since it only runs at GC time. It’s useful for cleaning up data used by a shared library or something (e.g. something malloc’d by a C lib), but it only solves part of the problem.

    I’m talking about scope guards. In Rust, here’s how you deal with mutexes:

    {
        let value = mutex.Lock();
        ... use value ...
        // mutex.Unlock() automatically called
    } 
    

    The closest thing in Go is defer():

    mutex.Lock()
    defer mutex.Unlock()
    

    That works most of the time, but it doesn’t handle more complex use cases, like selectively unlocking a mutex early while still guaranteeing it eventually gets unlocked.

    Rust fixes this with the Drop trait, so basically I can drop something early conditionally, but it’ll get dropped automatically when going out of scope. For example:

    struct A(String);
    
    impl Drop for A {
        fn drop(&mut self) {
            println!("dropping {}", self.0)
        }
    }
    
    fn main() {
        let a = A("a".into());
        let b = A("b".into());
        let c = A("c".into());
        drop(b);
    }
    

    Without the last line, this prints c, b, a, i.e. stack order. With the last line, it instead prints b, c, a, because I drop b early.

    This is incredibly useful when dealing with complex logic, especially with mutexes, because it allows you to cleanly and correctly handle edge cases. Things are dropped at block scope too, giving even more control of semantically releasing things like locks.

    That said, 1.24 added WASM, which is really cool, so thanks for encouraging me to look at the release notes.





  • I let the GPTs do that for me, without worrying that I won’t learn to code YAML for Ansible.

    And this is the perfect use case. There’s a good chance someone has done exactly what you want, and AI can regurgitate that for you.

    That’s not true of any interesting software project though.

    FAIL some code reviews on corner cases. Fail some reviews on ISO27002 and supply chain and role sep. Fail some deployments when they’re using dev tools in prod. And use them all as teachable moments.

    Fortunately, I work at an org that does this. It turns out that if our product breaks in prod, our customers could lose millions, which means they could go to a competitor. We build software to satisfy regulators, regulators that have the power to shut down everything if the ts aren’t crossed just so.

    Maybe that’s the problem, maybe the stakes are low enough that quality isn’t important anymore. Idk, what I do know is that I go hard on reviews.


  • To be fair, YAML sucks. It’s a config language that someone thought should cover everything, but excel at nothing.

    Just use TOML, JSON, or old-school INI. YAML will just give you an aneurism. Use the best tool for the job, which is often not the prettiest one.

    Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.

    Antoine de Saint-Exupéry

    Kids these days with their fancy stuff, you don’t need all that to write good software. YAML is the quintessential “jack of all trades, master of none” nonsense. It’s a config file, just make it easy to parse and document how to edit it. That’s it.


  • Really? My kids are hitting the rules hard. In 1st grade, they’re learning pronunciation rules I never learned (that’s phonics, right?). My 2nd grader is reading the 4th Harry Potter book, and my 5th grader finished the whole series in 3rd grade and is reading at a 7th or 8th grade level.

    I did teach them to read before kindergarten (just used a book for 2-3 months of 10 min lessons), but that’s it, everything else is school and personal interest. They can both type reasonably well because they use the Minecraft console and chat. They’re great at puzzles, and my 5th grader beat me at chess (I tried a wonky opening, and he punished me), which they learned at school (extra curricular, but run by a teacher).

    We love our charter school, though I don’t think it’s that different from the public school.



  • Same. It can generate credible-looking code, but I don’t find it very useful. Here’s what I’ve tried:

    • describe a function - takes longer to read the explanation than grok the code
    • generate tests - hallucinates arguments, doesn’t do proper boundary checks, etc
    • looking up docs - mostly useful to find search terms for the real docs

    The second was kind of useful since it provided the structure, but I still replaced 90% of it.

    I’m still messing with it, but beyond solving “blank page syndrome,” it’s not that great. And for that, I mostly just copy something from elsewhere in the project anyway, which is often faster than going to the LLM.

    I’m really bad at explaining what I want, because by the time I can do that, it’s faster to just build it. That said, I’m a senior dev, so I’ve been around the block a bit.


  • We do two “code challenges”:

    1. Very simple, many are done in 5 min; this just weeds out the incompetent applicants, and 90% of the code is written (i.e. simulate working in an existing codebase)
    2. Ambiguous requirements, the point is to ask questions, and we actually have different branches depending on assumptions they made (to challenge their assumptions); i.e. simulate building a solution with product team

    The first is in the first round, the second is in the technical interview. Neither are difficult, and we provide any equations they’ll need.

    It’s much more important that they can reason about requirements than code something quick, because life won’t give you firm requirements, and we don’t want a ton of back and forth with product team if we can avoid it, so we need to catch most of that at the start.

    In short, we’re looking for actual software engineers, not code monkeys.


  • Go is fine, but it has its flaws. I prefer Rust because:

    • memory safety is a compiler check, not a runtime check, so you catch issues earlier
    • locks contain their values, so you can’t accidentally do anything unsafe
    • no nil (() is semantically different), so no surprises with contracts
    • everything is an expression, which lends itself really well to FP concepts
    • actual dependency management at 1.0
    • pretty much no runtime, so calling from another language is super easy
    • targets WASM and microcontrollers
    • no pointers (not exactly true)

    It takes longer to learn, but I’m about as productive with both now.


  • The GC in Go is fantastic IMO since it runs in a separate thread. I used it since 1.0 (switched our product from node.js), and dealt with all the the pain of an imprecise GC (fixed in 1.5?) and all the little improvements to arrive at it’s current state.

    The main issues I have with it are pretty core to the language, unfortunately, such as:

    • interface{} is basically a void*, but since it’s a fat pointer, it can hold nil without itself being nil, which can happen by accident
    • runtime reflection is a bad habit, but it’s unfortunately really common
    • it’s really easy to deadlock by making stupid mistakes; if it had automatic unlocking based on scope (like Rust, or something like Python’s context managers), we could solve this, but defer just isn’t good enough
    • no destructors - with destructors, we could build a solution to deadlocks

    Maybe they fixed some of those issues, idk, I haven’t used it for several years. I did use it for about 10 years though.