let statements

(* At top level, you can use `;;` (as if you were in a REPL) to end a `let` statement *)
let x=... and y=...;;code here

(* You can also chain let statements without any `;;` or `in` in between. *)
(* (This is only at top level, you cannot do this in a function, loop, etc.) *)
let x=... let y=...;;code here

(* Sometimes instead of `and` you can use tuples *)
let x=ref 2 and y=ref 3;;
let x,y=ref 2,ref 3;;

Redefining/defining operators

(Pretty much same as F#.) When defining functions, you can use an operator instead of a function name to often save bytes. You can either define a new one if you run out of space (add on another char like *+), or just redefine an existing one (+/-/*///^/etc). Here's a precedence table.

For unary operators, the only one-byte option I know of is (!), but you can also redefine unary plus and minus with (~+) and (~-) (you can still call with -/+).

let(%)a b=a+b;;
print_int(1%6)

let(!)a=a+1;;
print_int !3

Conditionals

If you are using if with a side effect, you can usually shorten it with && and ||. Since both sides have to be booleans, you can just compare it with () (which is unit).

if something then print_int 3 else print_string"hello";
something&&print_int 3=()||print_string"hello"=()

if something then a:=2;
something&&(a:=2)=();

Avoid reusing module names

List.length(List.filter(...)a);
List.(length(filter(...)a));

Sometimes it's shorter to use open instead if using this syntax causes you to need a lot of ;;s instead of in or something:

let a=something;;let b=a+2;;Module.func(Module.func b)
Module.(let a=something in let b=a+2 in func(func b))
open Module;let a=something;;let b=a+2;;func(func b)

Printing hardcoded numbers

String.iter(fun x->Printf.printf"%d
"(Char.code x))"byte sequence"

Unfortunately with Unicode, this prints the bytes themselves instead of the codepoints. But it works if it's in ASCII.