Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

>with Python, you have to hope that your integration tests are sufficiently exhaustive to catch this

If there is a border between code I trust and code I don't trust I put some sort of sanity type-checking to catch this type of problem early. This usually gives me immediate notification of certain classes of bug when a test invokes that code.

I also put other forms of sanity checking in that are not type-specific - e.g. check a file exists before passing it on to a method that will read it.

I am not sure if this style of programming entirely mimics haskell's main USP in python, but it sure makes tracking down certain kinds of error much quicker.

Integration tests attack the problem from one direction and strict type checking (whether static or dynamic) attacks the problem from the other direction.

IMHO you have to attack from both directions. Ever increasing integration test coverage is obviously subject to diminishing returns (in terms of bugs caught/investment), but so is ever stricter type systems.



>If there is a border between code I trust and code I don't trust I put some sort of sanity type-checking to catch this type of problem early. This usually gives me immediate notification of certain classes of bug when a test invokes that code.

That's still on a "due dilligence" basis, and too late.


>too late.

If you are severely lacking in test coverage. Otherwise no, it's not too late.

Slow compilers that take ~2-10 minutes in statically typed languages give slower feedback than test suites that take seconds in dynamically typed languages. This inhibits development speed.


As an abstract statement, I can only agree. In reality, modern compilers can do some incrementally. So when you make a change in file A, the compiler will only change things dependent on A. In practice this chains a small number of file recompiles. The end result is an instantaneous feedback loop.

I get this all the time in Eclipse. Eclipse doesn't really try to do a full compile. It just updates as above. This allows me to see within a few ms if my code compiles. If it doesn't, red line and the reason.

A full compile on a about 40k classes without tests (just compile, static checking) with Maven on my 2013 MBP is about 5 seconds. So still pretty fast. Probably equivalently as a fast as your dynamic language + tests.

The same is true for hot swapping code. The JVM supports this a bit. Spring Boot supports it more. JRebel supports it entirely. When making a micro service or even a website in Spring Boot STS (Eclipse + Spring features), the run time will restart. Clocked time is 1.2 seconds. Not too bad.

For changes to just HTML/CSS/templates/ etc, no reboot time.


> e.g. check a file exists before passing it on to a method that will read it.

Is this not subject to a race condition though? I.e. you check that the file exists, get a green go-ahead flag to execute your method which will read it, by which time some external entity has caused the file to be removed. So then you handle the inevitable errors that are thrown by the OS when you try to access a non-existent file, making the first check redundant.

Or, to provide another example. A recent project I'm working on acts as a control interface for a process running on the same machine, which exposes an HTTP service. When the application starts up, it can check that the process it wants to control is running, but it is inevitable that at some point, the process will get killed while my application is running, and therefore I have to prepare to gracefully handle that case. I may as well unify everything down to that single path of error handling.


>Is this not subject to a race condition though? I.e. you check that the file exists, get a green go-ahead flag to execute your method which will read it, by which time some external entity has caused the file to be removed. So then you handle the inevitable errors that are thrown by the OS when you try to access a non-existent file, making the first check redundant.

It's not intended to catch every edge case. It's intended to act as a sanity check and a barrier to code executing under known invalid conditions.

Point being that if your code is operating under certain assumptions (e.g. config file is present), it makes sense to check those assumptions early and fail fast, hard and clearly if they don't.

>Or, to provide another example. A recent project I'm working on acts as a control interface for a process running on the same machine, which exposes an HTTP service. When the application starts up, it can check that the process it wants to control is running, but it is inevitable that at some point, the process will get killed while my application is running, and therefore I have to prepare to gracefully handle that case.

This is in essence what I was saying: do some sanity checking and report coherent error messages (i.e. was the server even up?) up front before continuing down a code path.

That way you get a clear actionable error early rather than a mess of weird behavior that means you have to play detective to figure out what went wrong.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: