Three methods, no fewer
When built-in layers don't cover your architecture, subclass keras.layers.Layer. Three methods to implement:
__init__— store config (units, activation, etc.). Do NOT create weights here.build(input_shape)— create weights here. Called once on first forward pass when input shape is finally known. Useself.add_weight(...)— never rawtf.Variablein Keras 3.call(inputs)— forward computation.
For serialization (saving/loading), also implement get_config() — return a dict that from_config() can use to reconstruct the layer.
Keras 3 breaking change: use
self.add_weight(), not tf.Variable. Raw tf.Variable objects are no longer auto-tracked by Keras 3's variable tracking. Using them inside a layer makes weights invisible to the optimizer — your model trains nothing and you'll wonder why.