First of all, it will not break type inference and you can define multiple records with the same fields as well as multiple variants with the same constructors in the same module. It is not recommended in general but is perfectly fine and well-defined.
When you define two records that share some field names, the last definition of a field hides the previous definitions, e.g.,
type t1 = {x : int}
type t2 = {x : char}
let x {x} = x
The x function will have type t2 -> char. It is because type inference is syntax-driven and from the name of a field it goes to the type of its record. In modern versions of OCaml, you can use type-directed constructor disambiguation to access the shadowed fields, e.g.,
let x : t1 -> _ = fun {x} -> x
Since we constrained the type, the compiler will look for the field in the specified record.
This is a rather new feature and before it, there were only two options for field name disambiguation. In fact, both are still the options and probably are recommended.
The first option is to put each record in its own module. This is my personal preference. In fact, I don't like having records or variants in my interfaces at all and always pack them in modules and hide them under interfaces as an implementation detail.
module T1 = struct
type t = {x : int}
end
module T2 = struct
type t = {x : char}
end
let x_of_t1 {T1.x} = x
let x_of_t2 {T2.x} = x
But it is even better to provide val x : t -> _ function in each module and use the module interface without relying on the internal representation.
The next and the last option is to use good old and ugly prefixes to disambiguate the names. Not really a solution (as the names are just now different) but it is quite common and is not that bad as it looks like, e.g.,
type t1 = {t1_x : int}
type t2 = {t2_x : char}
I personally dislike this solution, but it is quite common amongst various program analysis projects where you have lots of intermediate languages all sharing the same constructs but with different payloads. E.g., a constant Pconst of string * loc, which comes from the parse tree, vs. a constant in the abstract syntax tree (AST) Aconst of int * loc vs. a constant in the typed AST Tconst of int * typ * loc, and so on. Using prefixes here is practical, cf. T.Const vs Tconst, an economy of one character! :), and well-established tradition. Though I would personally still prefer modules to all other options.