Turn an ice-cream cone into a pyramid
One of the problems I have seen and shared with most of my colleagues is the application of TDD/BDD in a real life scenario. Most of the time, you have to modify/maintain a system that contains a lot of integration tests, automated GUI tests or manual tests. This situation could lead to an ice-cream cone anti-pattern as illustrated below.

Ice-cream cone
From a developer’s perspective, this situation is painful as it is often time consuming. For example, when you want to add a new feature to the system, ensuring that this addition does not break anything takes time. Furthermore, this time wasting severely disrupts the TDD system flow. Having a lot of integration tests or end-to-end tests makes your suite tests more brittle. In general, this can lead to false negative errors. Generally, these errors are generated by the test itself, not by the problem you are trying to resolve. Therefore, you have to spend some time figuring out what is going on. Moreover, one of the worst test results are if the error is random. This is because the team cannot trust the reliability of the tests which could lead to a lack of confidence in applying TDD/BDD. Now, we are conscious of this potential problem, what strategy can we employ to resolve this situation? TestPyramid.
TestPyramid
In summary this concept inverts the cone. Your unit tests should cover all the business rules of the system. We should make sure we are using the same language, in this case a unit test:
A unit test is a test that ‘runs in isolation’ from the other tests and does not integrate with ‘shared fixtures’ (e.g. file, database).
If your write your user stories as a unit test, you have a powerful test suite which enables you to continually modify the code and subsequently run your tests. Using this approach, you can write your test outside-in focusing on the behaviour of the system and avoiding the verification of the implementation details. Does this mean the whole tests should be unit tests? Effectively we should have different types of tests depending on the objective. However, as the following diagram demonstrates, the difference should be focused on the objective of code coverage that should be achieved:

There is a notion that states you should not base your software design on tests needs. For example, you should not expose internal details to validation, in this case it is obvious why you should not do it: you are breaking the encapsulation principle. However, we have to bear in mind that one of the quality attributes that the software has to achieve is that it should be testable. Moreover, the more testable a system, the more decoupled it is. Therefore, when we design software we should aim for testable architectures.
The talk TDD: where did it all go wrong by Ian Cooper, explores this topic. As Ian Cooper explains, Hexagonal architecture is one of the architectures to achieve the ideal TestPyramid.
What is Hexagonal architecture?
It was defined by Alistair Cockburn as follows:
Create your application to work without either a UI or a database so you can run automated regression-tests against the application, work when the database becomes unavailable, and link applications together without any user involvement. Alistair Cockburn

Basically, you define a core which implements the user stories/use cases of the application. This core provides interfaces to plug specific technology/frameworks that provide access to inputs and outputs as well as defining the boundaries of your system. In this architecture, the business logic does not depend on external modules. In contrast, the external modules should depend on the business logic. Therefore your core logic is decoupled from the infrastructure making it easier to test independently from external dependencies (shared fixtures).

Dependency inversion is the principle behind this architecture. The diagram above illustrates this perfectly as it shows that the dependencies are inverted.
This approach contrasts with traditional Layered Architecture that provides you with the flexibility to change your database engine. But in my experience this is rarely the case in the real world. A more common situation is that you want to update/change the library/framework that gives you access to the database or to use the new ‘revolutionary’ framework. With Layered Architecture this situation is harder because your business logic module depends on the database module. In contrast, Hexagonal Architecture offers you the flexibility to change technologies/frameworks without affecting the application’s core.