A SystemVerilog interface is a distributed state machine, with the state represented by its variables, partitioned into subsets managed by two or more cooperating modules. At the clock, each module first observes the variables it doesn’t manage, and then updates the variables it does manage. The purpose of the interface is to compose the modules into a larger machine that can still be reasoned about.
For example, a module might be given a modport
modport mp1( import observe1, update1, input a, b, c, output x, y, z);
and an interface method
function automatic void update1( ... ); x <= ...; y <= ...; z <= ...; endfunction
and use it in a procedure like
always @(posedge clk) begin ... ifc.update1(...); end
When interface-based design goes wrong, it’s usually because interfaces are pushed beyond their intended purpose. There’s a reason this construct is called ‘interface’, not ‘interconnnect’. If you want to model, say, a channel operating across a network-on-chip, use a module, and connect it with simple interfaces to both ends. Interfaces make it easy to configure the module different ways, such as a fast simulation model using bounded mailboxes and a synthesizable model. As long as both versions support the same modports, the communicating blocks won’t even notice the difference.