Why are software engineers so bad at defining good abstractions?